aboutsummaryrefslogtreecommitdiffhomepage
path: root/client/src/app/+admin
diff options
context:
space:
mode:
authorRigel Kent <sendmemail@rigelk.eu>2020-06-02 20:50:42 +0200
committerRigel Kent <sendmemail@rigelk.eu>2020-06-10 21:12:05 +0200
commit5baee5fca418487e72ddcd6419d31bca8659b668 (patch)
tree6604cc16d42152f4929d888565d2d435e2480d47 /client/src/app/+admin
parentd840487fed32b4604b02030c0d7464afa925904f (diff)
downloadPeerTube-5baee5fca418487e72ddcd6419d31bca8659b668.tar.gz
PeerTube-5baee5fca418487e72ddcd6419d31bca8659b668.tar.zst
PeerTube-5baee5fca418487e72ddcd6419d31bca8659b668.zip
rename blacklist to block/blocklist, merge block and auto-block views
- also replace whitelist with allowlist - add advanced filters for video-block-list view - move icons in video-block-list and video-abuse-list to left side for visibility - add robot icon to depict automated nature of a block in video-block-list resolves #2790
Diffstat (limited to 'client/src/app/+admin')
-rw-r--r--client/src/app/+admin/admin.component.ts6
-rw-r--r--client/src/app/+admin/admin.module.ts6
-rw-r--r--client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.html10
-rw-r--r--client/src/app/+admin/moderation/index.ts3
-rw-r--r--client/src/app/+admin/moderation/moderation.component.html6
-rw-r--r--client/src/app/+admin/moderation/moderation.component.ts8
-rw-r--r--client/src/app/+admin/moderation/moderation.routes.ts48
-rw-r--r--client/src/app/+admin/moderation/video-abuse-list/video-abuse-details.component.html2
-rw-r--r--client/src/app/+admin/moderation/video-abuse-list/video-abuse-list.component.html6
-rw-r--r--client/src/app/+admin/moderation/video-abuse-list/video-abuse-list.component.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.html20
-rw-r--r--client/src/app/+admin/moderation/video-auto-blacklist-list/video-auto-blacklist-list.component.scss14
-rw-r--r--client/src/app/+admin/moderation/video-auto-blacklist-list/video-auto-blacklist-list.component.ts86
-rw-r--r--client/src/app/+admin/moderation/video-blacklist-list/index.ts1
-rw-r--r--client/src/app/+admin/moderation/video-blacklist-list/video-blacklist-list.component.html100
-rw-r--r--client/src/app/+admin/moderation/video-blacklist-list/video-blacklist-list.component.ts113
-rw-r--r--client/src/app/+admin/moderation/video-block-list/index.ts1
-rw-r--r--client/src/app/+admin/moderation/video-block-list/video-block-list.component.html113
-rw-r--r--client/src/app/+admin/moderation/video-block-list/video-block-list.component.scss18
-rw-r--r--client/src/app/+admin/moderation/video-block-list/video-block-list.component.ts196
-rw-r--r--client/src/app/+admin/users/user-edit/user-edit.ts2
-rw-r--r--client/src/app/+admin/users/user-edit/user-update.component.ts2
23 files changed, 382 insertions, 397 deletions
diff --git a/client/src/app/+admin/admin.component.ts b/client/src/app/+admin/admin.component.ts
index 9662522dc..4cf3da0e8 100644
--- a/client/src/app/+admin/admin.component.ts
+++ b/client/src/app/+admin/admin.component.ts
@@ -18,7 +18,7 @@ export class AdminComponent implements OnInit {
18 ngOnInit () { 18 ngOnInit () {
19 if (this.hasUsersRight()) this.items.push({ label: this.i18n('Users'), routerLink: '/admin/users' }) 19 if (this.hasUsersRight()) this.items.push({ label: this.i18n('Users'), routerLink: '/admin/users' })
20 if (this.hasServerFollowRight()) this.items.push({ label: this.i18n('Follows & redundancies'), routerLink: '/admin/follows' }) 20 if (this.hasServerFollowRight()) this.items.push({ label: this.i18n('Follows & redundancies'), routerLink: '/admin/follows' })
21 if (this.hasVideoAbusesRight() || this.hasVideoBlacklistRight()) this.items.push({ label: this.i18n('Moderation'), routerLink: '/admin/moderation' }) 21 if (this.hasVideoAbusesRight() || this.hasVideoBlocklistRight()) this.items.push({ label: this.i18n('Moderation'), routerLink: '/admin/moderation' })
22 if (this.hasConfigRight()) this.items.push({ label: this.i18n('Configuration'), routerLink: '/admin/config' }) 22 if (this.hasConfigRight()) this.items.push({ label: this.i18n('Configuration'), routerLink: '/admin/config' })
23 if (this.hasPluginsRight()) this.items.push({ label: this.i18n('Plugins/Themes'), routerLink: '/admin/plugins' }) 23 if (this.hasPluginsRight()) this.items.push({ label: this.i18n('Plugins/Themes'), routerLink: '/admin/plugins' })
24 if (this.hasJobsRight() || this.hasLogsRight() || this.hasDebugRight()) this.items.push({ label: this.i18n('System'), routerLink: '/admin/system' }) 24 if (this.hasJobsRight() || this.hasLogsRight() || this.hasDebugRight()) this.items.push({ label: this.i18n('System'), routerLink: '/admin/system' })
@@ -36,8 +36,8 @@ export class AdminComponent implements OnInit {
36 return this.auth.getUser().hasRight(UserRight.MANAGE_VIDEO_ABUSES) 36 return this.auth.getUser().hasRight(UserRight.MANAGE_VIDEO_ABUSES)
37 } 37 }
38 38
39 hasVideoBlacklistRight () { 39 hasVideoBlocklistRight () {
40 return this.auth.getUser().hasRight(UserRight.MANAGE_VIDEO_BLACKLIST) 40 return this.auth.getUser().hasRight(UserRight.MANAGE_VIDEO_BLOCKS)
41 } 41 }
42 42
43 hasConfigRight () { 43 hasConfigRight () {
diff --git a/client/src/app/+admin/admin.module.ts b/client/src/app/+admin/admin.module.ts
index d04313c0a..eb073f709 100644
--- a/client/src/app/+admin/admin.module.ts
+++ b/client/src/app/+admin/admin.module.ts
@@ -11,8 +11,7 @@ import { UserCreateComponent, UserListComponent, UserPasswordComponent, UsersCom
11import { 11import {
12 ModerationCommentModalComponent, 12 ModerationCommentModalComponent,
13 VideoAbuseListComponent, 13 VideoAbuseListComponent,
14 VideoAutoBlacklistListComponent, 14 VideoBlockListComponent
15 VideoBlacklistListComponent
16} from './moderation' 15} from './moderation'
17import { ModerationComponent } from '@app/+admin/moderation/moderation.component' 16import { ModerationComponent } from '@app/+admin/moderation/moderation.component'
18import { RedundancyCheckboxComponent } from '@app/+admin/follows/shared/redundancy-checkbox.component' 17import { RedundancyCheckboxComponent } from '@app/+admin/follows/shared/redundancy-checkbox.component'
@@ -59,10 +58,9 @@ import { VideoAbuseDetailsComponent } from './moderation/video-abuse-list/video-
59 UserListComponent, 58 UserListComponent,
60 59
61 ModerationComponent, 60 ModerationComponent,
62 VideoBlacklistListComponent, 61 VideoBlockListComponent,
63 VideoAbuseListComponent, 62 VideoAbuseListComponent,
64 VideoAbuseDetailsComponent, 63 VideoAbuseDetailsComponent,
65 VideoAutoBlacklistListComponent,
66 ModerationCommentModalComponent, 64 ModerationCommentModalComponent,
67 InstanceServerBlocklistComponent, 65 InstanceServerBlocklistComponent,
68 InstanceAccountBlocklistComponent, 66 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 981b6685f..52c759fb7 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
@@ -430,7 +430,7 @@
430 <div class="form-group"> 430 <div class="form-group">
431 <my-peertube-checkbox 431 <my-peertube-checkbox
432 inputName="autoBlacklistVideosOfUsersEnabled" formControlName="enabled" 432 inputName="autoBlacklistVideosOfUsersEnabled" formControlName="enabled"
433 i18n-labelText labelText="Blacklist new videos automatically" 433 i18n-labelText labelText="Block new videos automatically"
434 > 434 >
435 <ng-container ngProjectAs="description"> 435 <ng-container ngProjectAs="description">
436 <span i18n>Unless a user is marked as trusted, their videos will stay private until a moderator reviews them.</span> 436 <span i18n>Unless a user is marked as trusted, their videos will stay private until a moderator reviews them.</span>
@@ -671,16 +671,16 @@
671 <div class="form-group"> 671 <div class="form-group">
672 <my-peertube-checkbox inputName="servicesTwitterWhitelisted" formControlName="whitelisted"> 672 <my-peertube-checkbox inputName="servicesTwitterWhitelisted" formControlName="whitelisted">
673 <ng-template ptTemplate="label"> 673 <ng-template ptTemplate="label">
674 <ng-container i18n>Instance whitelisted by Twitter</ng-container> 674 <ng-container i18n>Instance allowed by Twitter</ng-container>
675 </ng-template> 675 </ng-template>
676 676
677 <ng-template ptTemplate="help"> 677 <ng-template ptTemplate="help">
678 <ng-container i18n> 678 <ng-container i18n>
679 If your instance is whitelisted by Twitter, a video player will be embedded in the Twitter feed on PeerTube video share.<br /> 679 If your instance is explicitly allowed by Twitter, a video player will be embedded in the Twitter feed on PeerTube video share.<br />
680 If the instance is not whitelisted, we use an image link card that will redirect on your PeerTube instance.<br /><br /> 680 If the instance is not, we use an image link card that will redirect on your PeerTube instance.<br /><br />
681 Check this checkbox, save the configuration and test with a video URL of your instance (https://example.com/videos/watch/blabla) on 681 Check this checkbox, save the configuration and test with a video URL of your instance (https://example.com/videos/watch/blabla) on
682 <a target='_blank' rel='noopener noreferrer' href='https://cards-dev.twitter.com/validator'>https://cards-dev.twitter.com/validator</a> 682 <a target='_blank' rel='noopener noreferrer' href='https://cards-dev.twitter.com/validator'>https://cards-dev.twitter.com/validator</a>
683 to see if you instance is whitelisted. 683 to see if you instance is allowed.
684 </ng-container> 684 </ng-container>
685 </ng-template> 685 </ng-template>
686 </my-peertube-checkbox> 686 </my-peertube-checkbox>
diff --git a/client/src/app/+admin/moderation/index.ts b/client/src/app/+admin/moderation/index.ts
index 3c683a28c..e99244b74 100644
--- a/client/src/app/+admin/moderation/index.ts
+++ b/client/src/app/+admin/moderation/index.ts
@@ -1,5 +1,4 @@
1export * from './video-abuse-list' 1export * from './video-abuse-list'
2export * from './video-auto-blacklist-list' 2export * from './video-block-list'
3export * from './video-blacklist-list'
4export * from './moderation.component' 3export * from './moderation.component'
5export * from './moderation.routes' 4export * from './moderation.routes'
diff --git a/client/src/app/+admin/moderation/moderation.component.html b/client/src/app/+admin/moderation/moderation.component.html
index b70027957..09f149c0e 100644
--- a/client/src/app/+admin/moderation/moderation.component.html
+++ b/client/src/app/+admin/moderation/moderation.component.html
@@ -2,11 +2,9 @@
2 <div i18n class="form-sub-title">Moderation</div> 2 <div i18n class="form-sub-title">Moderation</div>
3 3
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 reports</a>
6 6
7 <a *ngIf="hasVideoBlacklistRight()" i18n routerLink="video-blacklist/list" routerLinkActive="active">{{ autoBlacklistVideosEnabled ? 'Manually blacklisted videos' : 'Blacklisted videos' }}</a> 7 <a *ngIf="hasVideoBlocklistRight()" i18n routerLink="video-blocks/list" routerLinkActive="active">Video blocks</a>
8
9 <a *ngIf="autoBlacklistVideosEnabled && hasVideoBlacklistRight()" i18n routerLink="video-auto-blacklist/list" routerLinkActive="active">Auto-blacklisted videos</a>
10 8
11 <a *ngIf="hasAccountsBlocklistRight()" i18n routerLink="blocklist/accounts" routerLinkActive="active">Muted accounts</a> 9 <a *ngIf="hasAccountsBlocklistRight()" i18n routerLink="blocklist/accounts" routerLinkActive="active">Muted accounts</a>
12 10
diff --git a/client/src/app/+admin/moderation/moderation.component.ts b/client/src/app/+admin/moderation/moderation.component.ts
index 7744deb06..d48305eed 100644
--- a/client/src/app/+admin/moderation/moderation.component.ts
+++ b/client/src/app/+admin/moderation/moderation.component.ts
@@ -7,7 +7,7 @@ import { AuthService, ServerService } from '@app/core'
7 styleUrls: [ './moderation.component.scss' ] 7 styleUrls: [ './moderation.component.scss' ]
8}) 8})
9export class ModerationComponent implements OnInit { 9export class ModerationComponent implements OnInit {
10 autoBlacklistVideosEnabled = false 10 autoBlockVideosEnabled = false
11 11
12 constructor ( 12 constructor (
13 private auth: AuthService, 13 private auth: AuthService,
@@ -16,7 +16,7 @@ export class ModerationComponent implements OnInit {
16 16
17 ngOnInit (): void { 17 ngOnInit (): void {
18 this.serverService.getConfig() 18 this.serverService.getConfig()
19 .subscribe(config => this.autoBlacklistVideosEnabled = config.autoBlacklist.videos.ofUsers.enabled) 19 .subscribe(config => this.autoBlockVideosEnabled = config.autoBlacklist.videos.ofUsers.enabled)
20 20
21 } 21 }
22 22
@@ -24,8 +24,8 @@ export class ModerationComponent implements OnInit {
24 return this.auth.getUser().hasRight(UserRight.MANAGE_VIDEO_ABUSES) 24 return this.auth.getUser().hasRight(UserRight.MANAGE_VIDEO_ABUSES)
25 } 25 }
26 26
27 hasVideoBlacklistRight () { 27 hasVideoBlocklistRight () {
28 return this.auth.getUser().hasRight(UserRight.MANAGE_VIDEO_BLACKLIST) 28 return this.auth.getUser().hasRight(UserRight.MANAGE_VIDEO_BLOCKS)
29 } 29 }
30 30
31 hasAccountsBlocklistRight () { 31 hasAccountsBlocklistRight () {
diff --git a/client/src/app/+admin/moderation/moderation.routes.ts b/client/src/app/+admin/moderation/moderation.routes.ts
index a024f2bee..aeb555c4a 100644
--- a/client/src/app/+admin/moderation/moderation.routes.ts
+++ b/client/src/app/+admin/moderation/moderation.routes.ts
@@ -2,8 +2,7 @@ import { Routes } from '@angular/router'
2import { UserRight } from '../../../../../shared' 2import { 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 { VideoBlockListComponent } from '@app/+admin/moderation/video-block-list'
6import { VideoAutoBlacklistListComponent } from '@app/+admin/moderation/video-auto-blacklist-list'
7import { ModerationComponent } from '@app/+admin/moderation/moderation.component' 6import { ModerationComponent } from '@app/+admin/moderation/moderation.component'
8import { InstanceAccountBlocklistComponent, InstanceServerBlocklistComponent } from '@app/+admin/moderation/instance-blocklist' 7import { InstanceAccountBlocklistComponent, InstanceServerBlocklistComponent } from '@app/+admin/moderation/instance-blocklist'
9 8
@@ -23,45 +22,44 @@ export const ModerationRoutes: Routes = [
23 pathMatch: 'full' 22 pathMatch: 'full'
24 }, 23 },
25 { 24 {
26 path: 'video-blacklist',
27 redirectTo: 'video-blacklist/list',
28 pathMatch: 'full'
29 },
30 {
31 path: 'video-auto-blacklist',
32 redirectTo: 'video-auto-blacklist/list',
33 pathMatch: 'full'
34 },
35 {
36 path: 'video-abuses/list', 25 path: 'video-abuses/list',
37 component: VideoAbuseListComponent, 26 component: VideoAbuseListComponent,
38 canActivate: [ UserRightGuard ], 27 canActivate: [ UserRightGuard ],
39 data: { 28 data: {
40 userRight: UserRight.MANAGE_VIDEO_ABUSES, 29 userRight: UserRight.MANAGE_VIDEO_ABUSES,
41 meta: { 30 meta: {
42 title: 'Video abuses list' 31 title: 'Video reports'
43 } 32 }
44 } 33 }
45 }, 34 },
46 { 35 {
36 path: 'video-blacklist',
37 redirectTo: 'video-blocks/list',
38 pathMatch: 'full'
39 },
40 {
41 path: 'video-auto-blacklist',
42 redirectTo: 'video-blocks/list',
43 pathMatch: 'full'
44 },
45 {
47 path: 'video-auto-blacklist/list', 46 path: 'video-auto-blacklist/list',
48 component: VideoAutoBlacklistListComponent, 47 redirectTo: 'video-blocks/list',
49 canActivate: [ UserRightGuard ], 48 pathMatch: 'full'
50 data: { 49 },
51 userRight: UserRight.MANAGE_VIDEO_BLACKLIST, 50 {
52 meta: { 51 path: 'video-blacklist',
53 title: 'Auto-blacklisted videos' 52 redirectTo: 'video-blocks/list',
54 } 53 pathMatch: 'full'
55 }
56 }, 54 },
57 { 55 {
58 path: 'video-blacklist/list', 56 path: 'video-blocks/list',
59 component: VideoBlacklistListComponent, 57 component: VideoBlockListComponent,
60 canActivate: [ UserRightGuard ], 58 canActivate: [ UserRightGuard ],
61 data: { 59 data: {
62 userRight: UserRight.MANAGE_VIDEO_BLACKLIST, 60 userRight: UserRight.MANAGE_VIDEO_BLOCKS,
63 meta: { 61 meta: {
64 title: 'Blacklisted videos' 62 title: 'Videos blocked'
65 } 63 }
66 } 64 }
67 }, 65 },
diff --git a/client/src/app/+admin/moderation/video-abuse-list/video-abuse-details.component.html b/client/src/app/+admin/moderation/video-abuse-list/video-abuse-details.component.html
index 2abcc0669..453a282d1 100644
--- a/client/src/app/+admin/moderation/video-abuse-list/video-abuse-details.component.html
+++ b/client/src/app/+admin/moderation/video-abuse-list/video-abuse-details.component.html
@@ -69,7 +69,7 @@
69 <div class="screenratio"> 69 <div class="screenratio">
70 <div *ngIf="videoAbuse.video.deleted || videoAbuse.video.blacklisted"> 70 <div *ngIf="videoAbuse.video.deleted || videoAbuse.video.blacklisted">
71 <span i18n *ngIf="videoAbuse.video.deleted">The video was deleted</span> 71 <span i18n *ngIf="videoAbuse.video.deleted">The video was deleted</span>
72 <span i18n *ngIf="!videoAbuse.video.deleted">The video was blacklisted</span> 72 <span i18n *ngIf="!videoAbuse.video.deleted">The video was blocked</span>
73 </div> 73 </div>
74 <div *ngIf="!videoAbuse.video.deleted && !videoAbuse.video.blacklisted" [innerHTML]="videoAbuse.embedHtml"></div> 74 <div *ngIf="!videoAbuse.video.deleted && !videoAbuse.video.blacklisted" [innerHTML]="videoAbuse.embedHtml"></div>
75 </div> 75 </div>
diff --git a/client/src/app/+admin/moderation/video-abuse-list/video-abuse-list.component.html b/client/src/app/+admin/moderation/video-abuse-list/video-abuse-list.component.html
index d30475794..df15999ec 100644
--- a/client/src/app/+admin/moderation/video-abuse-list/video-abuse-list.component.html
+++ b/client/src/app/+admin/moderation/video-abuse-list/video-abuse-list.component.html
@@ -19,7 +19,7 @@
19 <a [routerLink]="[ '/admin/moderation/video-abuses/list' ]" [queryParams]="{ 'search': 'state:pending' }" class="dropdown-item" i18n>Unsolved reports</a> 19 <a [routerLink]="[ '/admin/moderation/video-abuses/list' ]" [queryParams]="{ 'search': 'state:pending' }" class="dropdown-item" i18n>Unsolved reports</a>
20 <a [routerLink]="[ '/admin/moderation/video-abuses/list' ]" [queryParams]="{ 'search': 'state:accepted' }" class="dropdown-item" i18n>Accepted reports</a> 20 <a [routerLink]="[ '/admin/moderation/video-abuses/list' ]" [queryParams]="{ 'search': 'state:accepted' }" class="dropdown-item" i18n>Accepted reports</a>
21 <a [routerLink]="[ '/admin/moderation/video-abuses/list' ]" [queryParams]="{ 'search': 'state:rejected' }" class="dropdown-item" i18n>Refused reports</a> 21 <a [routerLink]="[ '/admin/moderation/video-abuses/list' ]" [queryParams]="{ 'search': 'state:rejected' }" class="dropdown-item" i18n>Refused reports</a>
22 <a [routerLink]="[ '/admin/moderation/video-abuses/list' ]" [queryParams]="{ 'search': 'videoIs:blacklisted' }" class="dropdown-item" i18n>Reports with blacklisted videos</a> 22 <a [routerLink]="[ '/admin/moderation/video-abuses/list' ]" [queryParams]="{ 'search': 'videoIs:blacklisted' }" class="dropdown-item" i18n>Reports with blocked videos</a>
23 <a [routerLink]="[ '/admin/moderation/video-abuses/list' ]" [queryParams]="{ 'search': 'videoIs:deleted' }" class="dropdown-item" i18n>Reports with deleted videos</a> 23 <a [routerLink]="[ '/admin/moderation/video-abuses/list' ]" [queryParams]="{ 'search': 'videoIs:deleted' }" class="dropdown-item" i18n>Reports with deleted videos</a>
24 </div> 24 </div>
25 </div> 25 </div>
@@ -84,9 +84,9 @@
84 </div> 84 </div>
85 <div class="video-table-video-text"> 85 <div class="video-table-video-text">
86 <div> 86 <div>
87 {{ videoAbuse.video.name }}
88 <span *ngIf="!videoAbuse.video.blacklisted" class="glyphicon glyphicon-new-window"></span> 87 <span *ngIf="!videoAbuse.video.blacklisted" class="glyphicon glyphicon-new-window"></span>
89 <span *ngIf="videoAbuse.video.blacklisted" i18n-title title="Video was blacklisted" class="glyphicon glyphicon-ban-circle"></span> 88 <span *ngIf="videoAbuse.video.blacklisted" i18n-title title="The video was blocked" class="glyphicon glyphicon-ban-circle"></span>
89 {{ videoAbuse.video.name }}
90 </div> 90 </div>
91 <div class="text-muted" i18n>by {{ videoAbuse.video.channel?.displayName }} on {{ videoAbuse.video.channel?.host }} </div> 91 <div class="text-muted" i18n>by {{ videoAbuse.video.channel?.displayName }} on {{ videoAbuse.video.channel?.host }} </div>
92 </div> 92 </div>
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 39f619cc3..ca37bccf3 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
@@ -3,7 +3,7 @@ import { Account } from '@app/shared/account/account.model'
3import { Notifier } from '@app/core' 3import { Notifier } from '@app/core'
4import { SortMeta } from 'primeng/api' 4import { SortMeta } from 'primeng/api'
5import { VideoAbuse, VideoAbuseState } from '../../../../../../shared' 5import { VideoAbuse, VideoAbuseState } from '../../../../../../shared'
6import { RestPagination, RestTable, VideoAbuseService, VideoBlacklistService } from '../../../shared' 6import { RestPagination, RestTable, VideoAbuseService, VideoBlockService } 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 { ConfirmService } from '../../../core/index' 9import { ConfirmService } from '../../../core/index'
@@ -53,7 +53,7 @@ export class VideoAbuseListComponent extends RestTable implements OnInit, AfterV
53 private videoAbuseService: VideoAbuseService, 53 private videoAbuseService: VideoAbuseService,
54 private blocklistService: BlocklistService, 54 private blocklistService: BlocklistService,
55 private videoService: VideoService, 55 private videoService: VideoService,
56 private videoBlacklistService: VideoBlacklistService, 56 private videoBlocklistService: VideoBlockService,
57 private confirmService: ConfirmService, 57 private confirmService: ConfirmService,
58 private i18n: I18n, 58 private i18n: I18n,
59 private markdownRenderer: MarkdownService, 59 private markdownRenderer: MarkdownService,
@@ -101,13 +101,13 @@ export class VideoAbuseListComponent extends RestTable implements OnInit, AfterV
101 isDisplayed: videoAbuse => !videoAbuse.video.deleted 101 isDisplayed: videoAbuse => !videoAbuse.video.deleted
102 }, 102 },
103 { 103 {
104 label: this.i18n('Blacklist video'), 104 label: this.i18n('Block video'),
105 isDisplayed: videoAbuse => !videoAbuse.video.deleted && !videoAbuse.video.blacklisted, 105 isDisplayed: videoAbuse => !videoAbuse.video.deleted && !videoAbuse.video.blacklisted,
106 handler: videoAbuse => { 106 handler: videoAbuse => {
107 this.videoBlacklistService.blacklistVideo(videoAbuse.video.id, undefined, true) 107 this.videoBlocklistService.blockVideo(videoAbuse.video.id, undefined, true)
108 .subscribe( 108 .subscribe(
109 () => { 109 () => {
110 this.notifier.success(this.i18n('Video blacklisted.')) 110 this.notifier.success(this.i18n('Video blocklisted.'))
111 111
112 this.updateVideoAbuseState(videoAbuse, VideoAbuseState.ACCEPTED) 112 this.updateVideoAbuseState(videoAbuse, VideoAbuseState.ACCEPTED)
113 }, 113 },
@@ -117,13 +117,13 @@ export class VideoAbuseListComponent extends RestTable implements OnInit, AfterV
117 } 117 }
118 }, 118 },
119 { 119 {
120 label: this.i18n('Unblacklist video'), 120 label: this.i18n('Unblock video'),
121 isDisplayed: videoAbuse => !videoAbuse.video.deleted && videoAbuse.video.blacklisted, 121 isDisplayed: videoAbuse => !videoAbuse.video.deleted && videoAbuse.video.blacklisted,
122 handler: videoAbuse => { 122 handler: videoAbuse => {
123 this.videoBlacklistService.removeVideoFromBlacklist(videoAbuse.video.id) 123 this.videoBlocklistService.unblockVideo(videoAbuse.video.id)
124 .subscribe( 124 .subscribe(
125 () => { 125 () => {
126 this.notifier.success(this.i18n('Video unblacklisted.')) 126 this.notifier.success(this.i18n('Video unblocklisted.'))
127 127
128 this.updateVideoAbuseState(videoAbuse, VideoAbuseState.ACCEPTED) 128 this.updateVideoAbuseState(videoAbuse, VideoAbuseState.ACCEPTED)
129 }, 129 },
@@ -292,7 +292,6 @@ export class VideoAbuseListComponent extends RestTable implements OnInit, AfterV
292 292
293 err => this.notifier.error(err.message) 293 err => this.notifier.error(err.message)
294 ) 294 )
295
296 } 295 }
297 296
298 protected loadData () { 297 protected loadData () {
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
deleted file mode 100644
index e3522f68c..000000000
--- a/client/src/app/+admin/moderation/video-auto-blacklist-list/index.ts
+++ /dev/null
@@ -1 +0,0 @@
1export * 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
deleted file mode 100644
index e2193b630..000000000
--- a/client/src/app/+admin/moderation/video-auto-blacklist-list/video-auto-blacklist-list.component.html
+++ /dev/null
@@ -1,20 +0,0 @@
1<my-videos-selection
2 [pagination]="pagination"
3 [(selection)]="selection"
4 [(videosModel)]="videos"
5 [miniatureDisplayOptions]="miniatureDisplayOptions"
6 [titlePage]="titlePage"
7 [getVideosObservableFunction]="getVideosObservableFunction"
8>
9 <ng-template ptTemplate="globalButtons">
10 <span class="action-button action-button-unblacklist-selection" (click)="removeSelectedVideosFromBlacklist()">
11 <my-global-icon iconName="tick"></my-global-icon>
12 <ng-container i18n>Unblacklist</ng-container>
13 </span>
14 </ng-template>
15
16 <ng-template ptTemplate="rowButtons" let-video>
17 <my-button i18n-label label="Unblacklist" icon="tick" (click)="removeVideoFromBlacklist(video)"></my-button>
18 </ng-template>
19
20</my-videos-selection>
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
deleted file mode 100644
index 85ebc6041..000000000
--- a/client/src/app/+admin/moderation/video-auto-blacklist-list/video-auto-blacklist-list.component.scss
+++ /dev/null
@@ -1,14 +0,0 @@
1@import '_variables';
2@import '_mixins';
3
4.action-button-unblacklist-selection {
5 display: inline-block;
6
7 @include peertube-button;
8 @include orange-button;
9 @include button-with-icon(21px);
10
11 my-global-icon {
12 @include apply-svg-color(#fff);
13 }
14}
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
deleted file mode 100644
index fb2962b47..000000000
--- a/client/src/app/+admin/moderation/video-auto-blacklist-list/video-auto-blacklist-list.component.ts
+++ /dev/null
@@ -1,86 +0,0 @@
1import { Component } from '@angular/core'
2import { I18n } from '@ngx-translate/i18n-polyfill'
3import { ActivatedRoute, Router } from '@angular/router'
4import { ComponentPagination } from '@app/shared/rest/component-pagination.model'
5import { AuthService, Notifier, ServerService } from '@app/core'
6import { VideoBlacklistService } from '@app/shared'
7import { immutableAssign } from '@app/shared/misc/utils'
8import { ScreenService } from '@app/shared/misc/screen.service'
9import { MiniatureDisplayOptions } from '@app/shared/video/video-miniature.component'
10import { SelectionType } from '@app/shared/video/videos-selection.component'
11import { Video } from '@app/shared/video/video.model'
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 {
19 titlePage: string
20 selection: SelectionType = {}
21 miniatureDisplayOptions: MiniatureDisplayOptions = {
22 date: true,
23 views: false,
24 by: true,
25 privacyLabel: false,
26 privacyText: true,
27 state: false,
28 blacklistInfo: false,
29 nsfw: true
30 }
31 pagination: ComponentPagination = {
32 currentPage: 1,
33 itemsPerPage: 5,
34 totalItems: null
35 }
36 videos: Video[] = []
37 getVideosObservableFunction = this.getVideosObservable.bind(this)
38
39 constructor (
40 protected router: Router,
41 protected route: ActivatedRoute,
42 protected notifier: Notifier,
43 protected authService: AuthService,
44 protected screenService: ScreenService,
45 protected serverService: ServerService,
46 private i18n: I18n,
47 private videoBlacklistService: VideoBlacklistService
48 ) {
49 this.titlePage = this.i18n('Auto-blacklisted videos')
50 }
51
52 getVideosObservable (page: number) {
53 const newPagination = immutableAssign(this.pagination, { currentPage: page })
54
55 return this.videoBlacklistService.getAutoBlacklistedAsVideoList(newPagination)
56 }
57
58 removeVideoFromBlacklist (entry: Video) {
59 this.videoBlacklistService.removeVideoFromBlacklist(entry.id).subscribe(
60 () => {
61 this.notifier.success(this.i18n('Video {{name}} removed from blacklist.', { name: entry.name }))
62
63 this.videos = this.videos.filter(v => v.id !== entry.id)
64 },
65
66 error => this.notifier.error(error.message)
67 )
68 }
69
70 removeSelectedVideosFromBlacklist () {
71 const toReleaseVideosIds = Object.keys(this.selection)
72 .filter(k => this.selection[ k ] === true)
73 .map(k => parseInt(k, 10))
74
75 this.videoBlacklistService.removeVideoFromBlacklist(toReleaseVideosIds).subscribe(
76 () => {
77 this.notifier.success(this.i18n('{{num}} videos removed from blacklist.', { num: toReleaseVideosIds.length }))
78
79 this.selection = {}
80 this.videos = this.videos.filter(v => toReleaseVideosIds.includes(v.id) === false)
81 },
82
83 error => this.notifier.error(error.message)
84 )
85 }
86}
diff --git a/client/src/app/+admin/moderation/video-blacklist-list/index.ts b/client/src/app/+admin/moderation/video-blacklist-list/index.ts
deleted file mode 100644
index 4daf64187..000000000
--- a/client/src/app/+admin/moderation/video-blacklist-list/index.ts
+++ /dev/null
@@ -1 +0,0 @@
1export * from './video-blacklist-list.component'
diff --git a/client/src/app/+admin/moderation/video-blacklist-list/video-blacklist-list.component.html b/client/src/app/+admin/moderation/video-blacklist-list/video-blacklist-list.component.html
deleted file mode 100644
index cfa04514f..000000000
--- a/client/src/app/+admin/moderation/video-blacklist-list/video-blacklist-list.component.html
+++ /dev/null
@@ -1,100 +0,0 @@
1<p-table
2 [value]="blacklist" [lazy]="true" [paginator]="totalRecords > 0" [totalRecords]="totalRecords" [rows]="rowsPerPage" [rowsPerPageOptions]="rowsPerPageOptions"
3 [sortField]="sort.field" [sortOrder]="sort.order" (onLazyLoad)="loadLazy($event)" dataKey="id"
4 [showCurrentPageReport]="true" i18n-currentPageReportTemplate
5 currentPageReportTemplate="Showing {{'{first}'}} to {{'{last}'}} of {{'{totalRecords}'}} blacklisted videos"
6 (onPage)="onPage($event)" [expandedRowKeys]="expandedRows"
7>
8 <ng-template pTemplate="caption">
9 <div class="caption">
10 <div class="ml-auto has-feedback has-clear">
11 <input
12 type="text" name="table-filter" id="table-filter" i18n-placeholder placeholder="Filter..."
13 (keyup)="onSearch($event)"
14 >
15 <a class="glyphicon glyphicon-remove-sign form-control-feedback form-control-clear" (click)="resetSearch()"></a>
16 <span class="sr-only" i18n>Clear filters</span>
17 </div>
18 </div>
19 </ng-template>
20
21 <ng-template pTemplate="header">
22 <tr>
23 <th style="width: 40px"></th>
24 <th i18n pSortableColumn="name">Video <p-sortIcon field="name"></p-sortIcon></th>
25 <th style="width: 100px;" i18n>Sensitive</th>
26 <th style="width: 120px;" i18n>Unfederated</th>
27 <th style="width: 150px;" i18n pSortableColumn="createdAt">Date <p-sortIcon field="createdAt"></p-sortIcon></th>
28 <th style="width: 150px;"></th>
29 </tr>
30 </ng-template>
31
32 <ng-template pTemplate="body" let-videoBlacklist let-expanded="expanded">
33 <tr>
34 <td *ngIf="!videoBlacklist.reason"></td>
35 <td *ngIf="videoBlacklist.reason" class="expand-cell c-hand" [pRowToggler]="videoBlacklist" i18n-ngbTooltip ngbTooltip="More information" placement="top-left" container="body">
36 <span class="expander">
37 <i [ngClass]="expanded ? 'glyphicon glyphicon-menu-down' : 'glyphicon glyphicon-menu-right'"></i>
38 </span>
39 </td>
40
41 <td>
42 <a [href]="getVideoUrl(videoBlacklist)" class="video-table-video-link" i18n-title title="Open video in a new tab" target="_blank" rel="noopener noreferrer">
43 <div class="video-table-video">
44 <div class="video-table-video-image">
45 <img [src]="videoBlacklist.video.thumbnailPath">
46 </div>
47 <div class="video-table-video-text">
48 <div>
49 {{ videoBlacklist.video.name }}
50 <span i18n-title title="Video was blacklisted" class="glyphicon glyphicon-ban-circle"></span>
51 </div>
52 <div class="text-muted">by {{ videoBlacklist.video.channel?.displayName }} on {{ videoBlacklist.video.channel?.host }} </div>
53 </div>
54 </div>
55 </a>
56 </td>
57
58 <ng-container *ngIf="videoBlacklist.reason">
59 <td class="c-hand" [pRowToggler]="videoBlacklist">{{ booleanToText(videoBlacklist.video.nsfw) }}</td>
60 <td class="c-hand" [pRowToggler]="videoBlacklist">{{ booleanToText(videoBlacklist.unfederated) }}</td>
61 <td class="c-hand" [pRowToggler]="videoBlacklist">{{ videoBlacklist.createdAt | date: 'short' }}</td>
62 </ng-container>
63 <ng-container *ngIf="!videoBlacklist.reason">
64 <td>{{ booleanToText(videoBlacklist.video.nsfw) }}</td>
65 <td>{{ booleanToText(videoBlacklist.unfederated) }}</td>
66 <td>{{ videoBlacklist.createdAt | date: 'short' }}</td>
67 </ng-container>
68
69 <td class="action-cell">
70 <my-action-dropdown
71 [ngClass]="{ 'show': expanded }" placement="bottom-right" container="body"
72 i18n-label label="Actions" [actions]="videoBlacklistActions" [entry]="videoBlacklist"
73 ></my-action-dropdown>
74 </td>
75 </tr>
76 </ng-template>
77
78 <ng-template pTemplate="rowexpansion" let-videoBlacklist>
79 <tr>
80 <td class="expand-cell" colspan="6">
81 <div class="d-flex moderation-expanded">
82 <span class="col-2 moderation-expanded-label" i18n>Blacklist reason:</span>
83 <span class="col-9 moderation-expanded-text" [innerHTML]="videoBlacklist.reasonHtml"></span>
84 </div>
85 </td>
86 </tr>
87 </ng-template>
88
89 <ng-template pTemplate="emptymessage">
90 <tr>
91 <td colspan="6">
92 <div class="empty-table-message">
93 <ng-container *ngIf="search" i18n>No blacklisted video found matching current filters.</ng-container>
94 <ng-container *ngIf="!search" i18n>No blacklisted video found.</ng-container>
95 </div>
96 </td>
97 </tr>
98 </ng-template>
99</p-table>
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
deleted file mode 100644
index 63ecdeb9f..000000000
--- a/client/src/app/+admin/moderation/video-blacklist-list/video-blacklist-list.component.ts
+++ /dev/null
@@ -1,113 +0,0 @@
1import { Component, OnInit } from '@angular/core'
2import { SortMeta } from 'primeng/api'
3import { Notifier, ServerService } from '@app/core'
4import { ConfirmService } from '../../../core'
5import { RestPagination, RestTable, VideoBlacklistService } from '../../../shared'
6import { VideoBlacklist, VideoBlacklistType } from '../../../../../../shared'
7import { I18n } from '@ngx-translate/i18n-polyfill'
8import { DropdownAction } from '../../../shared/buttons/action-dropdown.component'
9import { Video } from '../../../shared/video/video.model'
10import { MarkdownService } from '@app/shared/renderer'
11
12@Component({
13 selector: 'my-video-blacklist-list',
14 templateUrl: './video-blacklist-list.component.html',
15 styleUrls: [ '../moderation.component.scss' ]
16})
17export class VideoBlacklistListComponent extends RestTable implements OnInit {
18 blacklist: (VideoBlacklist & { reasonHtml?: string })[] = []
19 totalRecords = 0
20 sort: SortMeta = { field: 'createdAt', order: -1 }
21 pagination: RestPagination = { count: this.rowsPerPage, start: 0 }
22 listBlacklistTypeFilter: VideoBlacklistType = undefined
23
24 videoBlacklistActions: DropdownAction<VideoBlacklist>[] = []
25
26 constructor (
27 private notifier: Notifier,
28 private serverService: ServerService,
29 private confirmService: ConfirmService,
30 private videoBlacklistService: VideoBlacklistService,
31 private markdownRenderer: MarkdownService,
32 private i18n: I18n
33 ) {
34 super()
35 }
36
37 ngOnInit () {
38 this.serverService.getConfig()
39 .subscribe(config => {
40 // don't filter if auto-blacklist is not enabled as this will be the only list
41 if (config.autoBlacklist.videos.ofUsers.enabled) {
42 this.listBlacklistTypeFilter = VideoBlacklistType.MANUAL
43 }
44 })
45
46 this.initialize()
47
48 this.videoBlacklistActions = [
49 {
50 label: this.i18n('Unblacklist'),
51 handler: videoBlacklist => this.removeVideoFromBlacklist(videoBlacklist)
52 }
53 ]
54 }
55
56 getIdentifier () {
57 return 'VideoBlacklistListComponent'
58 }
59
60 getVideoUrl (videoBlacklist: VideoBlacklist) {
61 return Video.buildClientUrl(videoBlacklist.video.uuid)
62 }
63
64 booleanToText (value: boolean) {
65 if (value === true) return this.i18n('yes')
66
67 return this.i18n('no')
68 }
69
70 toHtml (text: string) {
71 return this.markdownRenderer.textMarkdownToHTML(text)
72 }
73
74 async removeVideoFromBlacklist (entry: VideoBlacklist) {
75 const confirmMessage = this.i18n(
76 'Do you really want to remove this video from the blacklist? It will be available again in the videos list.'
77 )
78
79 const res = await this.confirmService.confirm(confirmMessage, this.i18n('Unblacklist'))
80 if (res === false) return
81
82 this.videoBlacklistService.removeVideoFromBlacklist(entry.video.id).subscribe(
83 () => {
84 this.notifier.success(this.i18n('Video {{name}} removed from the blacklist.', { name: entry.video.name }))
85 this.loadData()
86 },
87
88 err => this.notifier.error(err.message)
89 )
90 }
91
92 protected loadData () {
93 this.videoBlacklistService.listBlacklist({
94 pagination: this.pagination,
95 sort: this.sort,
96 search: this.search,
97 type: this.listBlacklistTypeFilter
98 })
99 .subscribe(
100 async resultList => {
101 this.totalRecords = resultList.total
102
103 this.blacklist = resultList.data
104
105 for (const element of this.blacklist) {
106 Object.assign(element, { reasonHtml: await this.toHtml(element.reason) })
107 }
108 },
109
110 err => this.notifier.error(err.message)
111 )
112 }
113}
diff --git a/client/src/app/+admin/moderation/video-block-list/index.ts b/client/src/app/+admin/moderation/video-block-list/index.ts
new file mode 100644
index 000000000..ec4de8f62
--- /dev/null
+++ b/client/src/app/+admin/moderation/video-block-list/index.ts
@@ -0,0 +1 @@
export * from './video-block-list.component'
diff --git a/client/src/app/+admin/moderation/video-block-list/video-block-list.component.html b/client/src/app/+admin/moderation/video-block-list/video-block-list.component.html
new file mode 100644
index 000000000..f3ec37314
--- /dev/null
+++ b/client/src/app/+admin/moderation/video-block-list/video-block-list.component.html
@@ -0,0 +1,113 @@
1<p-table
2 [value]="blocklist" [lazy]="true" [paginator]="totalRecords > 0" [totalRecords]="totalRecords" [rows]="rowsPerPage" [rowsPerPageOptions]="rowsPerPageOptions"
3 [sortField]="sort.field" [sortOrder]="sort.order" (onLazyLoad)="loadLazy($event)" dataKey="id"
4 [showCurrentPageReport]="true" i18n-currentPageReportTemplate
5 currentPageReportTemplate="Showing {{'{first}'}} to {{'{last}'}} of {{'{totalRecords}'}} blocked videos"
6 (onPage)="onPage($event)" [expandedRowKeys]="expandedRows"
7>
8 <ng-template pTemplate="caption">
9 <div class="caption">
10 <div class="ml-auto">
11 <div class="input-group has-feedback has-clear">
12 <div class="input-group-prepend c-hand" ngbDropdown placement="bottom-left auto" container="body">
13 <div class="input-group-text" ngbDropdownToggle>
14 <span class="caret" aria-haspopup="menu" role="button"></span>
15 </div>
16
17 <div role="menu" ngbDropdownMenu>
18 <h6 class="dropdown-header" i18n>Advanced block filters</h6>
19 <a [routerLink]="[ '/admin/moderation/video-blocks/list' ]" [queryParams]="{ 'search': 'type:auto' }" class="dropdown-item" i18n>Automatic blocks</a>
20 <a [routerLink]="[ '/admin/moderation/video-blocks/list' ]" [queryParams]="{ 'search': 'type:manual' }" class="dropdown-item" i18n>Manual blocks</a>
21 </div>
22 </div>
23 <input
24 type="text" name="table-filter" id="table-filter" i18n-placeholder placeholder="Filter..."
25 (keyup)="onBlockSearch($event)"
26 >
27 <a class="glyphicon glyphicon-remove-sign form-control-feedback form-control-clear" (click)="resetTableFilter()"></a>
28 <span class="sr-only" i18n>Clear filters</span>
29 </div>
30 </div>
31 </div>
32 </ng-template>
33
34 <ng-template pTemplate="header">
35 <tr>
36 <th style="width: 40px"></th>
37 <th i18n pSortableColumn="name">Video <p-sortIcon field="name"></p-sortIcon></th>
38 <th style="width: 100px;" i18n>Sensitive</th>
39 <th style="width: 120px;" i18n>Unfederated</th>
40 <th style="width: 150px;" i18n pSortableColumn="createdAt">Date <p-sortIcon field="createdAt"></p-sortIcon></th>
41 <th style="width: 150px;"></th>
42 </tr>
43 </ng-template>
44
45 <ng-template pTemplate="body" let-videoBlock let-expanded="expanded">
46 <tr>
47 <td *ngIf="!videoBlock.reason"></td>
48 <td *ngIf="videoBlock.reason" class="expand-cell c-hand" [pRowToggler]="videoBlock" i18n-ngbTooltip ngbTooltip="More information" placement="top-left" container="body">
49 <span class="expander">
50 <i [ngClass]="expanded ? 'glyphicon glyphicon-menu-down' : 'glyphicon glyphicon-menu-right'"></i>
51 </span>
52 </td>
53
54 <td>
55 <a [href]="getVideoUrl(videoBlock)" class="video-table-video-link" i18n-title title="Open video in a new tab" target="_blank" rel="noopener noreferrer">
56 <div class="video-table-video">
57 <div class="video-table-video-image">
58 <img [src]="videoBlock.video.thumbnailPath">
59 </div>
60 <div class="video-table-video-text">
61 <div>
62 <my-global-icon i18n-title title="The video was blocked due to automatic blocking of new videos" *ngIf="videoBlock.type == 2" iconName="robot"></my-global-icon>
63 {{ videoBlock.video.name }}
64 </div>
65 <div class="text-muted">by {{ videoBlock.video.channel?.displayName }} on {{ videoBlock.video.channel?.host }} </div>
66 </div>
67 </div>
68 </a>
69 </td>
70
71 <ng-container *ngIf="videoBlock.reason">
72 <td class="c-hand" [pRowToggler]="videoBlock">{{ booleanToText(videoBlock.video.nsfw) }}</td>
73 <td class="c-hand" [pRowToggler]="videoBlock">{{ booleanToText(videoBlock.unfederated) }}</td>
74 <td class="c-hand" [pRowToggler]="videoBlock">{{ videoBlock.createdAt | date: 'short' }}</td>
75 </ng-container>
76 <ng-container *ngIf="!videoBlock.reason">
77 <td>{{ booleanToText(videoBlock.video.nsfw) }}</td>
78 <td>{{ booleanToText(videoBlock.unfederated) }}</td>
79 <td>{{ videoBlock.createdAt | date: 'short' }}</td>
80 </ng-container>
81
82 <td class="action-cell">
83 <my-action-dropdown
84 [ngClass]="{ 'show': expanded }" placement="bottom-right" container="body"
85 i18n-label label="Actions" [actions]="videoBlocklistActions" [entry]="videoBlock"
86 ></my-action-dropdown>
87 </td>
88 </tr>
89 </ng-template>
90
91 <ng-template pTemplate="rowexpansion" let-videoBlock>
92 <tr>
93 <td class="expand-cell" colspan="6">
94 <div class="d-flex moderation-expanded">
95 <span class="col-2 moderation-expanded-label" i18n>Block reason:</span>
96 <span class="col-9 moderation-expanded-text" [innerHTML]="videoBlock.reasonHtml"></span>
97 </div>
98 </td>
99 </tr>
100 </ng-template>
101
102 <ng-template pTemplate="emptymessage">
103 <tr>
104 <td colspan="6">
105 <div class="empty-table-message">
106 <ng-container *ngIf="search" i18n>No blocked video found matching current filters.</ng-container>
107 <ng-container *ngIf="!search" i18n>No blocked video found.</ng-container>
108 </div>
109 </td>
110 </tr>
111 </ng-template>
112</p-table>
113
diff --git a/client/src/app/+admin/moderation/video-block-list/video-block-list.component.scss b/client/src/app/+admin/moderation/video-block-list/video-block-list.component.scss
new file mode 100644
index 000000000..43a365608
--- /dev/null
+++ b/client/src/app/+admin/moderation/video-block-list/video-block-list.component.scss
@@ -0,0 +1,18 @@
1@import 'mixins';
2
3my-global-icon {
4 @include apply-svg-color(#7d7d7d);
5
6 width: 12px;
7 height: 12px;
8 position: relative;
9 top: -1px;
10}
11
12.input-group {
13 @include peertube-input-group(300px);
14
15 .dropdown-toggle::after {
16 margin-left: 0;
17 }
18}
diff --git a/client/src/app/+admin/moderation/video-block-list/video-block-list.component.ts b/client/src/app/+admin/moderation/video-block-list/video-block-list.component.ts
new file mode 100644
index 000000000..e72ab5348
--- /dev/null
+++ b/client/src/app/+admin/moderation/video-block-list/video-block-list.component.ts
@@ -0,0 +1,196 @@
1import { Component, OnInit } from '@angular/core'
2import { SortMeta } from 'primeng/api'
3import { Notifier, ServerService } from '@app/core'
4import { ConfirmService } from '../../../core'
5import { RestPagination, RestTable, VideoBlockService } from '../../../shared'
6import { VideoBlocklist, VideoBlockType } from '../../../../../../shared'
7import { I18n } from '@ngx-translate/i18n-polyfill'
8import { DropdownAction } from '../../../shared/buttons/action-dropdown.component'
9import { Video } from '../../../shared/video/video.model'
10import { MarkdownService } from '@app/shared/renderer'
11import { Params, ActivatedRoute, Router } from '@angular/router'
12import { filter, switchMap } from 'rxjs/operators'
13import { VideoService } from '@app/shared/video/video.service'
14
15@Component({
16 selector: 'my-video-block-list',
17 templateUrl: './video-block-list.component.html',
18 styleUrls: [ '../moderation.component.scss', './video-block-list.component.scss' ]
19})
20export class VideoBlockListComponent extends RestTable implements OnInit {
21 blocklist: (VideoBlocklist & { reasonHtml?: string })[] = []
22 totalRecords = 0
23 sort: SortMeta = { field: 'createdAt', order: -1 }
24 pagination: RestPagination = { count: this.rowsPerPage, start: 0 }
25 listBlockTypeFilter: VideoBlockType = undefined
26
27 videoBlocklistActions: DropdownAction<VideoBlocklist>[][] = []
28
29 constructor (
30 private notifier: Notifier,
31 private serverService: ServerService,
32 private confirmService: ConfirmService,
33 private videoBlocklistService: VideoBlockService,
34 private markdownRenderer: MarkdownService,
35 private videoService: VideoService,
36 private route: ActivatedRoute,
37 private router: Router,
38 private i18n: I18n
39 ) {
40 super()
41
42 this.videoBlocklistActions = [
43 [
44 {
45 label: this.i18n('Internal actions'),
46 isHeader: true
47 },
48 {
49 label: this.i18n('Switch video block to manual'),
50 handler: videoBlock => {
51 this.videoBlocklistService.unblockVideo(videoBlock.video.id).pipe(
52 switchMap(_ => this.videoBlocklistService.blockVideo(videoBlock.video.id, undefined, true))
53 ).subscribe(
54 () => {
55 this.notifier.success(this.i18n('Video {{name}} switched to manual block.', { name: videoBlock.video.name }))
56 this.loadData()
57 },
58
59 err => this.notifier.error(err.message)
60 )
61 }
62 }
63 ],
64 [
65 {
66 label: this.i18n('Actions for the video'),
67 isHeader: true,
68 },
69 {
70 label: this.i18n('Unblock video'),
71 handler: videoBlock => this.unblockVideo(videoBlock)
72 },
73
74 {
75 label: this.i18n('Delete video'),
76 handler: async videoBlock => {
77 const res = await this.confirmService.confirm(
78 this.i18n('Do you really want to delete this video?'),
79 this.i18n('Delete')
80 )
81 if (res === false) return
82
83 this.videoService.removeVideo(videoBlock.video.id)
84 .subscribe(
85 () => {
86 this.notifier.success(this.i18n('Video deleted.'))
87 },
88
89 err => this.notifier.error(err.message)
90 )
91 }
92 }
93 ]
94 ]
95 }
96
97 ngOnInit () {
98 this.serverService.getConfig()
99 .subscribe(config => {
100 // don't filter if auto-blacklist is not enabled as this will be the only list
101 if (config.autoBlacklist.videos.ofUsers.enabled) {
102 this.listBlockTypeFilter = VideoBlockType.MANUAL
103 }
104 })
105
106 this.initialize()
107
108 this.route.queryParams
109 .pipe(filter(params => params.search !== undefined && params.search !== null))
110 .subscribe(params => {
111 this.search = params.search
112 this.setTableFilter(params.search)
113 this.loadData()
114 })
115 }
116
117 ngAfterViewInit () {
118 if (this.search) this.setTableFilter(this.search)
119 }
120
121 /* Table filter functions */
122 onBlockSearch (event: Event) {
123 this.onSearch(event)
124 this.setQueryParams((event.target as HTMLInputElement).value)
125 }
126
127 setQueryParams (search: string) {
128 const queryParams: Params = {}
129 if (search) Object.assign(queryParams, { search })
130 this.router.navigate([ '/admin/moderation/video-blocks/list' ], { queryParams })
131 }
132
133 resetTableFilter () {
134 this.setTableFilter('')
135 this.setQueryParams('')
136 this.resetSearch()
137 }
138 /* END Table filter functions */
139
140 getIdentifier () {
141 return 'VideoBlockListComponent'
142 }
143
144 getVideoUrl (videoBlock: VideoBlocklist) {
145 return Video.buildClientUrl(videoBlock.video.uuid)
146 }
147
148 booleanToText (value: boolean) {
149 if (value === true) return this.i18n('yes')
150
151 return this.i18n('no')
152 }
153
154 toHtml (text: string) {
155 return this.markdownRenderer.textMarkdownToHTML(text)
156 }
157
158 async unblockVideo (entry: VideoBlocklist) {
159 const confirmMessage = this.i18n(
160 'Do you really want to unblock this video? It will be available again in the videos list.'
161 )
162
163 const res = await this.confirmService.confirm(confirmMessage, this.i18n('Unblock'))
164 if (res === false) return
165
166 this.videoBlocklistService.unblockVideo(entry.video.id).subscribe(
167 () => {
168 this.notifier.success(this.i18n('Video {{name}} unblocked.', { name: entry.video.name }))
169 this.loadData()
170 },
171
172 err => this.notifier.error(err.message)
173 )
174 }
175
176 protected loadData () {
177 this.videoBlocklistService.listBlocks({
178 pagination: this.pagination,
179 sort: this.sort,
180 search: this.search,
181 })
182 .subscribe(
183 async resultList => {
184 this.totalRecords = resultList.total
185
186 this.blocklist = resultList.data
187
188 for (const element of this.blocklist) {
189 Object.assign(element, { reasonHtml: await this.toHtml(element.reason) })
190 }
191 },
192
193 err => this.notifier.error(err.message)
194 )
195 }
196}
diff --git a/client/src/app/+admin/users/user-edit/user-edit.ts b/client/src/app/+admin/users/user-edit/user-edit.ts
index 6e2952c44..98249bcc1 100644
--- a/client/src/app/+admin/users/user-edit/user-edit.ts
+++ b/client/src/app/+admin/users/user-edit/user-edit.ts
@@ -88,7 +88,7 @@ export abstract class UserEdit extends FormReactive implements OnInit {
88 } 88 }
89 89
90 protected buildAdminFlags (formValue: any) { 90 protected buildAdminFlags (formValue: any) {
91 return formValue.byPassAutoBlacklist ? UserAdminFlag.BY_PASS_VIDEO_AUTO_BLACKLIST : UserAdminFlag.NONE 91 return formValue.byPassAutoBlacklist ? UserAdminFlag.BYPASS_VIDEO_AUTO_BLOCK : UserAdminFlag.NONE
92 } 92 }
93 93
94 protected buildQuotaOptions () { 94 protected buildQuotaOptions () {
diff --git a/client/src/app/+admin/users/user-edit/user-update.component.ts b/client/src/app/+admin/users/user-edit/user-update.component.ts
index e0e1fbddf..f2bd8c8ec 100644
--- a/client/src/app/+admin/users/user-edit/user-update.component.ts
+++ b/client/src/app/+admin/users/user-edit/user-update.component.ts
@@ -125,7 +125,7 @@ export class UserUpdateComponent extends UserEdit implements OnInit, OnDestroy {
125 role: userJson.role.toString(), 125 role: userJson.role.toString(),
126 videoQuota: userJson.videoQuota, 126 videoQuota: userJson.videoQuota,
127 videoQuotaDaily: userJson.videoQuotaDaily, 127 videoQuotaDaily: userJson.videoQuotaDaily,
128 byPassAutoBlacklist: userJson.adminFlags & UserAdminFlag.BY_PASS_VIDEO_AUTO_BLACKLIST 128 byPassAutoBlacklist: userJson.adminFlags & UserAdminFlag.BYPASS_VIDEO_AUTO_BLOCK
129 }) 129 })
130 } 130 }
131} 131}