diff options
author | Rigel Kent <sendmemail@rigelk.eu> | 2020-06-02 20:50:42 +0200 |
---|---|---|
committer | Rigel Kent <sendmemail@rigelk.eu> | 2020-06-10 21:12:05 +0200 |
commit | 5baee5fca418487e72ddcd6419d31bca8659b668 (patch) | |
tree | 6604cc16d42152f4929d888565d2d435e2480d47 /client/src/app/+admin | |
parent | d840487fed32b4604b02030c0d7464afa925904f (diff) | |
download | PeerTube-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')
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 | |||
11 | import { | 11 | import { |
12 | ModerationCommentModalComponent, | 12 | ModerationCommentModalComponent, |
13 | VideoAbuseListComponent, | 13 | VideoAbuseListComponent, |
14 | VideoAutoBlacklistListComponent, | 14 | VideoBlockListComponent |
15 | VideoBlacklistListComponent | ||
16 | } from './moderation' | 15 | } from './moderation' |
17 | import { ModerationComponent } from '@app/+admin/moderation/moderation.component' | 16 | import { ModerationComponent } from '@app/+admin/moderation/moderation.component' |
18 | import { RedundancyCheckboxComponent } from '@app/+admin/follows/shared/redundancy-checkbox.component' | 17 | import { 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 @@ | |||
1 | export * from './video-abuse-list' | 1 | export * from './video-abuse-list' |
2 | export * from './video-auto-blacklist-list' | 2 | export * from './video-block-list' |
3 | export * from './video-blacklist-list' | ||
4 | export * from './moderation.component' | 3 | export * from './moderation.component' |
5 | export * from './moderation.routes' | 4 | 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 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 | }) |
9 | export class ModerationComponent implements OnInit { | 9 | export 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' | |||
2 | import { UserRight } from '../../../../../shared' | 2 | import { UserRight } from '../../../../../shared' |
3 | import { UserRightGuard } from '@app/core' | 3 | import { UserRightGuard } from '@app/core' |
4 | import { VideoAbuseListComponent } from '@app/+admin/moderation/video-abuse-list' | 4 | import { VideoAbuseListComponent } from '@app/+admin/moderation/video-abuse-list' |
5 | import { VideoBlacklistListComponent } from '@app/+admin/moderation/video-blacklist-list' | 5 | import { VideoBlockListComponent } from '@app/+admin/moderation/video-block-list' |
6 | import { VideoAutoBlacklistListComponent } from '@app/+admin/moderation/video-auto-blacklist-list' | ||
7 | import { ModerationComponent } from '@app/+admin/moderation/moderation.component' | 6 | import { ModerationComponent } from '@app/+admin/moderation/moderation.component' |
8 | import { InstanceAccountBlocklistComponent, InstanceServerBlocklistComponent } from '@app/+admin/moderation/instance-blocklist' | 7 | import { 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' | |||
3 | import { Notifier } from '@app/core' | 3 | import { Notifier } from '@app/core' |
4 | import { SortMeta } from 'primeng/api' | 4 | import { SortMeta } from 'primeng/api' |
5 | import { VideoAbuse, VideoAbuseState } from '../../../../../../shared' | 5 | import { VideoAbuse, VideoAbuseState } from '../../../../../../shared' |
6 | import { RestPagination, RestTable, VideoAbuseService, VideoBlacklistService } from '../../../shared' | 6 | import { RestPagination, RestTable, VideoAbuseService, VideoBlockService } from '../../../shared' |
7 | import { I18n } from '@ngx-translate/i18n-polyfill' | 7 | import { I18n } from '@ngx-translate/i18n-polyfill' |
8 | import { DropdownAction } from '../../../shared/buttons/action-dropdown.component' | 8 | import { DropdownAction } from '../../../shared/buttons/action-dropdown.component' |
9 | import { ConfirmService } from '../../../core/index' | 9 | import { 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 @@ | |||
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 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 @@ | |||
1 | import { Component } from '@angular/core' | ||
2 | import { I18n } from '@ngx-translate/i18n-polyfill' | ||
3 | import { ActivatedRoute, Router } from '@angular/router' | ||
4 | import { ComponentPagination } from '@app/shared/rest/component-pagination.model' | ||
5 | import { AuthService, Notifier, ServerService } from '@app/core' | ||
6 | import { VideoBlacklistService } from '@app/shared' | ||
7 | import { immutableAssign } from '@app/shared/misc/utils' | ||
8 | import { ScreenService } from '@app/shared/misc/screen.service' | ||
9 | import { MiniatureDisplayOptions } from '@app/shared/video/video-miniature.component' | ||
10 | import { SelectionType } from '@app/shared/video/videos-selection.component' | ||
11 | import { 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 | }) | ||
18 | export 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 @@ | |||
1 | export * 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 @@ | |||
1 | import { Component, OnInit } from '@angular/core' | ||
2 | import { SortMeta } from 'primeng/api' | ||
3 | import { Notifier, ServerService } from '@app/core' | ||
4 | import { ConfirmService } from '../../../core' | ||
5 | import { RestPagination, RestTable, VideoBlacklistService } from '../../../shared' | ||
6 | import { VideoBlacklist, VideoBlacklistType } from '../../../../../../shared' | ||
7 | import { I18n } from '@ngx-translate/i18n-polyfill' | ||
8 | import { DropdownAction } from '../../../shared/buttons/action-dropdown.component' | ||
9 | import { Video } from '../../../shared/video/video.model' | ||
10 | import { 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 | }) | ||
17 | export 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 | |||
3 | my-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 @@ | |||
1 | import { Component, OnInit } from '@angular/core' | ||
2 | import { SortMeta } from 'primeng/api' | ||
3 | import { Notifier, ServerService } from '@app/core' | ||
4 | import { ConfirmService } from '../../../core' | ||
5 | import { RestPagination, RestTable, VideoBlockService } from '../../../shared' | ||
6 | import { VideoBlocklist, VideoBlockType } from '../../../../../../shared' | ||
7 | import { I18n } from '@ngx-translate/i18n-polyfill' | ||
8 | import { DropdownAction } from '../../../shared/buttons/action-dropdown.component' | ||
9 | import { Video } from '../../../shared/video/video.model' | ||
10 | import { MarkdownService } from '@app/shared/renderer' | ||
11 | import { Params, ActivatedRoute, Router } from '@angular/router' | ||
12 | import { filter, switchMap } from 'rxjs/operators' | ||
13 | import { 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 | }) | ||
20 | export 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 | } |