aboutsummaryrefslogtreecommitdiffhomepage
path: root/client/src
diff options
context:
space:
mode:
authorChocobozzz <me@florianbigard.com>2018-08-14 09:08:47 +0200
committerChocobozzz <me@florianbigard.com>2018-08-14 09:27:18 +0200
commit191764f30b0a812bf3a9dbdc7daf1d5afe25e12a (patch)
treea5592f8d89949cde832f025e393a3821ad2aca37 /client/src
parent26b7305a232e547709f433a6edf700bf495935d8 (diff)
downloadPeerTube-191764f30b0a812bf3a9dbdc7daf1d5afe25e12a.tar.gz
PeerTube-191764f30b0a812bf3a9dbdc7daf1d5afe25e12a.tar.zst
PeerTube-191764f30b0a812bf3a9dbdc7daf1d5afe25e12a.zip
Improve blacklist management
Diffstat (limited to 'client/src')
-rw-r--r--client/src/app/+admin/video-abuses/video-abuse-list/video-abuse-list.component.html2
-rw-r--r--client/src/app/+admin/video-abuses/video-abuse-list/video-abuse-list.component.ts5
-rw-r--r--client/src/app/+admin/video-blacklist/video-blacklist-list/video-blacklist-list.component.html11
-rw-r--r--client/src/app/+admin/video-blacklist/video-blacklist-list/video-blacklist-list.component.ts13
-rw-r--r--client/src/app/+my-account/my-account-videos/my-account-videos.component.html4
-rw-r--r--client/src/app/+my-account/my-account-videos/my-account-videos.component.scss17
-rw-r--r--client/src/app/+page-not-found/page-not-found.component.scss2
-rw-r--r--client/src/app/shared/video-blacklist/video-blacklist.service.ts6
-rw-r--r--client/src/app/shared/video/video-details.model.ts6
-rw-r--r--client/src/app/shared/video/video.model.ts9
-rw-r--r--client/src/app/videos/+video-watch/video-watch.component.html13
-rw-r--r--client/src/app/videos/+video-watch/video-watch.component.scss12
-rw-r--r--client/src/app/videos/+video-watch/video-watch.component.ts32
-rw-r--r--client/src/assets/images/global/undo.svg11
14 files changed, 124 insertions, 19 deletions
diff --git a/client/src/app/+admin/video-abuses/video-abuse-list/video-abuse-list.component.html b/client/src/app/+admin/video-abuses/video-abuse-list/video-abuse-list.component.html
index aa0e18c70..f213ab4b0 100644
--- a/client/src/app/+admin/video-abuses/video-abuse-list/video-abuse-list.component.html
+++ b/client/src/app/+admin/video-abuses/video-abuse-list/video-abuse-list.component.html
@@ -42,7 +42,7 @@
42 <td>{{ videoAbuse.createdAt }}</td> 42 <td>{{ videoAbuse.createdAt }}</td>
43 43
44 <td> 44 <td>
45 <a [href]="videoAbuse.video.url" i18n-title title="Go to the video" target="_blank" rel="noopener noreferrer"> 45 <a [href]="getVideoUrl(videoAbuse)" i18n-title title="Go to the video" target="_blank" rel="noopener noreferrer">
46 {{ videoAbuse.video.name }} 46 {{ videoAbuse.video.name }}
47 </a> 47 </a>
48 </td> 48 </td>
diff --git a/client/src/app/+admin/video-abuses/video-abuse-list/video-abuse-list.component.ts b/client/src/app/+admin/video-abuses/video-abuse-list/video-abuse-list.component.ts
index a850c0ec2..377e9c80f 100644
--- a/client/src/app/+admin/video-abuses/video-abuse-list/video-abuse-list.component.ts
+++ b/client/src/app/+admin/video-abuses/video-abuse-list/video-abuse-list.component.ts
@@ -8,6 +8,7 @@ import { I18n } from '@ngx-translate/i18n-polyfill'
8import { DropdownAction } from '@app/shared/buttons/action-dropdown.component' 8import { DropdownAction } from '@app/shared/buttons/action-dropdown.component'
9import { ConfirmService } from '@app/core' 9import { ConfirmService } from '@app/core'
10import { ModerationCommentModalComponent } from './moderation-comment-modal.component' 10import { ModerationCommentModalComponent } from './moderation-comment-modal.component'
11import { Video } from '@app/shared/video/video.model'
11 12
12@Component({ 13@Component({
13 selector: 'my-video-abuse-list', 14 selector: 'my-video-abuse-list',
@@ -79,6 +80,10 @@ export class VideoAbuseListComponent extends RestTable implements OnInit {
79 return videoAbuse.state.id === VideoAbuseState.REJECTED 80 return videoAbuse.state.id === VideoAbuseState.REJECTED
80 } 81 }
81 82
83 getVideoUrl (videoAbuse: VideoAbuse) {
84 return Video.buildClientUrl(videoAbuse.video.uuid)
85 }
86
82 async removeVideoAbuse (videoAbuse: VideoAbuse) { 87 async removeVideoAbuse (videoAbuse: VideoAbuse) {
83 const res = await this.confirmService.confirm(this.i18n('Do you really want to delete this abuse?'), this.i18n('Delete')) 88 const res = await this.confirmService.confirm(this.i18n('Do you really want to delete this abuse?'), this.i18n('Delete'))
84 if (res === false) return 89 if (res === false) return
diff --git a/client/src/app/+admin/video-blacklist/video-blacklist-list/video-blacklist-list.component.html b/client/src/app/+admin/video-blacklist/video-blacklist-list/video-blacklist-list.component.html
index 78989dc58..05b3a300c 100644
--- a/client/src/app/+admin/video-blacklist/video-blacklist-list/video-blacklist-list.component.html
+++ b/client/src/app/+admin/video-blacklist/video-blacklist-list/video-blacklist-list.component.html
@@ -10,8 +10,7 @@
10 <tr> 10 <tr>
11 <th style="width: 40px"></th> 11 <th style="width: 40px"></th>
12 <th i18n pSortableColumn="name">Video name <p-sortIcon field="name"></p-sortIcon></th> 12 <th i18n pSortableColumn="name">Video name <p-sortIcon field="name"></p-sortIcon></th>
13 <th i18n>NSFW</th> 13 <th i18n>Sensitive</th>
14 <th i18n>UUID</th>
15 <th i18n pSortableColumn="createdAt">Date <p-sortIcon field="createdAt"></p-sortIcon></th> 14 <th i18n pSortableColumn="createdAt">Date <p-sortIcon field="createdAt"></p-sortIcon></th>
16 <th style="width: 50px;"></th> 15 <th style="width: 50px;"></th>
17 </tr> 16 </tr>
@@ -25,9 +24,13 @@
25 </span> 24 </span>
26 </td> 25 </td>
27 26
28 <td>{{ videoBlacklist.video.name }}</td> 27 <td>
28 <a [href]="getVideoUrl(videoBlacklist)" i18n-title title="Go to the video" target="_blank" rel="noopener noreferrer">
29 {{ videoBlacklist.video.name }}
30 </a>
31 </td>
32
29 <td>{{ videoBlacklist.video.nsfw }}</td> 33 <td>{{ videoBlacklist.video.nsfw }}</td>
30 <td>{{ videoBlacklist.video.uuid }}</td>
31 <td>{{ videoBlacklist.createdAt }}</td> 34 <td>{{ videoBlacklist.createdAt }}</td>
32 35
33 <td class="action-cell"> 36 <td class="action-cell">
diff --git a/client/src/app/+admin/video-blacklist/video-blacklist-list/video-blacklist-list.component.ts b/client/src/app/+admin/video-blacklist/video-blacklist-list/video-blacklist-list.component.ts
index 00b0ac57e..0618252b8 100644
--- a/client/src/app/+admin/video-blacklist/video-blacklist-list/video-blacklist-list.component.ts
+++ b/client/src/app/+admin/video-blacklist/video-blacklist-list/video-blacklist-list.component.ts
@@ -3,9 +3,10 @@ import { SortMeta } from 'primeng/components/common/sortmeta'
3import { NotificationsService } from 'angular2-notifications' 3import { NotificationsService } from 'angular2-notifications'
4import { ConfirmService } from '../../../core' 4import { ConfirmService } from '../../../core'
5import { RestPagination, RestTable, VideoBlacklistService } from '../../../shared' 5import { RestPagination, RestTable, VideoBlacklistService } from '../../../shared'
6import { BlacklistedVideo } from '../../../../../../shared' 6import { VideoBlacklist } from '../../../../../../shared'
7import { I18n } from '@ngx-translate/i18n-polyfill' 7import { I18n } from '@ngx-translate/i18n-polyfill'
8import { DropdownAction } from '@app/shared/buttons/action-dropdown.component' 8import { DropdownAction } from '@app/shared/buttons/action-dropdown.component'
9import { Video } from '@app/shared/video/video.model'
9 10
10@Component({ 11@Component({
11 selector: 'my-video-blacklist-list', 12 selector: 'my-video-blacklist-list',
@@ -13,13 +14,13 @@ import { DropdownAction } from '@app/shared/buttons/action-dropdown.component'
13 styleUrls: [ './video-blacklist-list.component.scss' ] 14 styleUrls: [ './video-blacklist-list.component.scss' ]
14}) 15})
15export class VideoBlacklistListComponent extends RestTable implements OnInit { 16export class VideoBlacklistListComponent extends RestTable implements OnInit {
16 blacklist: BlacklistedVideo[] = [] 17 blacklist: VideoBlacklist[] = []
17 totalRecords = 0 18 totalRecords = 0
18 rowsPerPage = 10 19 rowsPerPage = 10
19 sort: SortMeta = { field: 'createdAt', order: 1 } 20 sort: SortMeta = { field: 'createdAt', order: 1 }
20 pagination: RestPagination = { count: this.rowsPerPage, start: 0 } 21 pagination: RestPagination = { count: this.rowsPerPage, start: 0 }
21 22
22 videoBlacklistActions: DropdownAction<BlacklistedVideo>[] = [] 23 videoBlacklistActions: DropdownAction<VideoBlacklist>[] = []
23 24
24 constructor ( 25 constructor (
25 private notificationsService: NotificationsService, 26 private notificationsService: NotificationsService,
@@ -41,7 +42,11 @@ export class VideoBlacklistListComponent extends RestTable implements OnInit {
41 this.loadSort() 42 this.loadSort()
42 } 43 }
43 44
44 async removeVideoFromBlacklist (entry: BlacklistedVideo) { 45 getVideoUrl (videoBlacklist: VideoBlacklist) {
46 return Video.buildClientUrl(videoBlacklist.video.uuid)
47 }
48
49 async removeVideoFromBlacklist (entry: VideoBlacklist) {
45 const confirmMessage = this.i18n( 50 const confirmMessage = this.i18n(
46 'Do you really want to remove this video from the blacklist? It will be available again in the videos list.' 51 'Do you really want to remove this video from the blacklist? It will be available again in the videos list.'
47 ) 52 )
diff --git a/client/src/app/+my-account/my-account-videos/my-account-videos.component.html b/client/src/app/+my-account/my-account-videos/my-account-videos.component.html
index 4823e2db9..8a6cb5c32 100644
--- a/client/src/app/+my-account/my-account-videos/my-account-videos.component.html
+++ b/client/src/app/+my-account/my-account-videos/my-account-videos.component.html
@@ -18,6 +18,10 @@
18 <a class="video-info-name" [routerLink]="['/videos/watch', video.uuid]" [attr.title]="video.name">{{ video.name }}</a> 18 <a class="video-info-name" [routerLink]="['/videos/watch', video.uuid]" [attr.title]="video.name">{{ video.name }}</a>
19 <span i18n class="video-info-date-views">{{ video.createdAt | myFromNow }} - {{ video.views | myNumberFormatter }} views</span> 19 <span i18n class="video-info-date-views">{{ video.createdAt | myFromNow }} - {{ video.views | myNumberFormatter }} views</span>
20 <div class="video-info-private">{{ video.privacy.label }}{{ getStateLabel(video) }}</div> 20 <div class="video-info-private">{{ video.privacy.label }}{{ getStateLabel(video) }}</div>
21 <div *ngIf="video.blacklisted" class="video-info-blacklisted">
22 <span class="blacklisted-label" i18n>Blacklisted</span>
23 <span class="blacklisted-reason" *ngIf="video.blacklistedReason">{{ video.blacklistedReason }}</span>
24 </div>
21 </div> 25 </div>
22 26
23 <!-- Display only once --> 27 <!-- Display only once -->
diff --git a/client/src/app/+my-account/my-account-videos/my-account-videos.component.scss b/client/src/app/+my-account/my-account-videos/my-account-videos.component.scss
index 9df28f472..64a04fa20 100644
--- a/client/src/app/+my-account/my-account-videos/my-account-videos.component.scss
+++ b/client/src/app/+my-account/my-account-videos/my-account-videos.component.scss
@@ -76,12 +76,25 @@
76 font-weight: $font-semibold; 76 font-weight: $font-semibold;
77 } 77 }
78 78
79 .video-info-date-views, .video-info-private { 79 .video-info-date-views,
80 .video-info-private,
81 .video-info-blacklisted {
80 font-size: 13px; 82 font-size: 13px;
81 83
82 &.video-info-private { 84 &.video-info-private,
85 &.video-info-blacklisted .blacklisted-label {
83 font-weight: $font-semibold; 86 font-weight: $font-semibold;
84 } 87 }
88
89 &.video-info-blacklisted {
90 color: red;
91
92 .blacklisted-reason {
93 &::before {
94 content: ' - ';
95 }
96 }
97 }
85 } 98 }
86 } 99 }
87 100
diff --git a/client/src/app/+page-not-found/page-not-found.component.scss b/client/src/app/+page-not-found/page-not-found.component.scss
index 53b6142e1..f3f0354a3 100644
--- a/client/src/app/+page-not-found/page-not-found.component.scss
+++ b/client/src/app/+page-not-found/page-not-found.component.scss
@@ -2,7 +2,7 @@ div {
2 height: 100%; 2 height: 100%;
3 width: 100%; 3 width: 100%;
4 text-align: center; 4 text-align: center;
5 margin-top: 150px; 5 padding-top: 150px;
6 6
7 font-size: 32px; 7 font-size: 32px;
8} \ No newline at end of file 8} \ No newline at end of file
diff --git a/client/src/app/shared/video-blacklist/video-blacklist.service.ts b/client/src/app/shared/video-blacklist/video-blacklist.service.ts
index a014260b1..7d39fd4f2 100644
--- a/client/src/app/shared/video-blacklist/video-blacklist.service.ts
+++ b/client/src/app/shared/video-blacklist/video-blacklist.service.ts
@@ -3,7 +3,7 @@ import { HttpClient, HttpParams } from '@angular/common/http'
3import { Injectable } from '@angular/core' 3import { Injectable } from '@angular/core'
4import { SortMeta } from 'primeng/components/common/sortmeta' 4import { SortMeta } from 'primeng/components/common/sortmeta'
5import { Observable } from 'rxjs' 5import { Observable } from 'rxjs'
6import { BlacklistedVideo, ResultList } from '../../../../../shared' 6import { VideoBlacklist, ResultList } from '../../../../../shared'
7import { environment } from '../../../environments/environment' 7import { environment } from '../../../environments/environment'
8import { RestExtractor, RestPagination, RestService } from '../rest' 8import { RestExtractor, RestPagination, RestService } from '../rest'
9 9
@@ -17,11 +17,11 @@ export class VideoBlacklistService {
17 private restExtractor: RestExtractor 17 private restExtractor: RestExtractor
18 ) {} 18 ) {}
19 19
20 listBlacklist (pagination: RestPagination, sort: SortMeta): Observable<ResultList<BlacklistedVideo>> { 20 listBlacklist (pagination: RestPagination, sort: SortMeta): Observable<ResultList<VideoBlacklist>> {
21 let params = new HttpParams() 21 let params = new HttpParams()
22 params = this.restService.addRestGetParams(params, pagination, sort) 22 params = this.restService.addRestGetParams(params, pagination, sort)
23 23
24 return this.authHttp.get<ResultList<BlacklistedVideo>>(VideoBlacklistService.BASE_VIDEOS_URL + 'blacklist', { params }) 24 return this.authHttp.get<ResultList<VideoBlacklist>>(VideoBlacklistService.BASE_VIDEOS_URL + 'blacklist', { params })
25 .pipe( 25 .pipe(
26 map(res => this.restExtractor.convertResultListDateToHuman(res)), 26 map(res => this.restExtractor.convertResultListDateToHuman(res)),
27 catchError(res => this.restExtractor.handleError(res)) 27 catchError(res => this.restExtractor.handleError(res))
diff --git a/client/src/app/shared/video/video-details.model.ts b/client/src/app/shared/video/video-details.model.ts
index bdcc0bbba..d346f985c 100644
--- a/client/src/app/shared/video/video-details.model.ts
+++ b/client/src/app/shared/video/video-details.model.ts
@@ -44,7 +44,11 @@ export class VideoDetails extends Video implements VideoDetailsServerModel {
44 } 44 }
45 45
46 isBlackistableBy (user: AuthUser) { 46 isBlackistableBy (user: AuthUser) {
47 return user && user.hasRight(UserRight.MANAGE_VIDEO_BLACKLIST) === true 47 return this.blacklisted !== true && user && user.hasRight(UserRight.MANAGE_VIDEO_BLACKLIST) === true
48 }
49
50 isUnblacklistableBy (user: AuthUser) {
51 return this.blacklisted === true && user && user.hasRight(UserRight.MANAGE_VIDEO_BLACKLIST) === true
48 } 52 }
49 53
50 isUpdatableBy (user: AuthUser) { 54 isUpdatableBy (user: AuthUser) {
diff --git a/client/src/app/shared/video/video.model.ts b/client/src/app/shared/video/video.model.ts
index 6b1a299ea..ec0afcccb 100644
--- a/client/src/app/shared/video/video.model.ts
+++ b/client/src/app/shared/video/video.model.ts
@@ -41,6 +41,8 @@ export class Video implements VideoServerModel {
41 waitTranscoding?: boolean 41 waitTranscoding?: boolean
42 state?: VideoConstant<VideoState> 42 state?: VideoConstant<VideoState>
43 scheduledUpdate?: VideoScheduleUpdate 43 scheduledUpdate?: VideoScheduleUpdate
44 blacklisted?: boolean
45 blacklistedReason?: string
44 46
45 account: { 47 account: {
46 id: number 48 id: number
@@ -62,6 +64,10 @@ export class Video implements VideoServerModel {
62 avatar: Avatar 64 avatar: Avatar
63 } 65 }
64 66
67 static buildClientUrl (videoUUID: string) {
68 return '/videos/watch/' + videoUUID
69 }
70
65 private static createDurationString (duration: number) { 71 private static createDurationString (duration: number) {
66 const hours = Math.floor(duration / 3600) 72 const hours = Math.floor(duration / 3600)
67 const minutes = Math.floor((duration % 3600) / 60) 73 const minutes = Math.floor((duration % 3600) / 60)
@@ -116,6 +122,9 @@ export class Video implements VideoServerModel {
116 122
117 this.scheduledUpdate = hash.scheduledUpdate 123 this.scheduledUpdate = hash.scheduledUpdate
118 if (this.state) this.state.label = peertubeTranslate(this.state.label, translations) 124 if (this.state) this.state.label = peertubeTranslate(this.state.label, translations)
125
126 this.blacklisted = hash.blacklisted
127 this.blacklistedReason = hash.blacklistedReason
119 } 128 }
120 129
121 isVideoNSFWForUser (user: User, serverConfig: ServerConfig) { 130 isVideoNSFWForUser (user: User, serverConfig: ServerConfig) {
diff --git a/client/src/app/videos/+video-watch/video-watch.component.html b/client/src/app/videos/+video-watch/video-watch.component.html
index f82f1c554..8d4a4a5ca 100644
--- a/client/src/app/videos/+video-watch/video-watch.component.html
+++ b/client/src/app/videos/+video-watch/video-watch.component.html
@@ -1,4 +1,4 @@
1<div class="row"> 1<div class="root-row row">
2 <!-- We need the video container for videojs so we just hide it --> 2 <!-- We need the video container for videojs so we just hide it -->
3 <div id="video-element-wrapper"> 3 <div id="video-element-wrapper">
4 <div *ngIf="remoteServerDown" class="remote-server-down"> 4 <div *ngIf="remoteServerDown" class="remote-server-down">
@@ -17,7 +17,12 @@
17 </div> 17 </div>
18 18
19 <div i18n class="alert alert-info" *ngIf="hasVideoScheduledPublication()"> 19 <div i18n class="alert alert-info" *ngIf="hasVideoScheduledPublication()">
20 This video will be published on {{ video.scheduledUpdate.updateAt | date: 'full' }} 20 This video will be published on {{ video.scheduledUpdate.updateAt | date: 'full' }}.
21 </div>
22
23 <div class="alert alert-danger" *ngIf="video?.blacklisted">
24 <div class="blacklisted-label" i18n>This video is blacklisted.</div>
25 {{ video.blacklistedReason }}
21 </div> 26 </div>
22 27
23 <!-- Video information --> 28 <!-- Video information -->
@@ -98,6 +103,10 @@
98 <span class="icon icon-blacklist"></span> <ng-container i18n>Blacklist</ng-container> 103 <span class="icon icon-blacklist"></span> <ng-container i18n>Blacklist</ng-container>
99 </a> 104 </a>
100 105
106 <a *ngIf="isVideoUnblacklistable()" class="dropdown-item" i18n-title title="Unblacklist this video" href="#" (click)="unblacklistVideo($event)">
107 <span class="icon icon-unblacklist"></span> <ng-container i18n>Unblacklist</ng-container>
108 </a>
109
101 <a *ngIf="isVideoRemovable()" class="dropdown-item" i18n-title title="Delete this video" href="#" (click)="removeVideo($event)"> 110 <a *ngIf="isVideoRemovable()" class="dropdown-item" i18n-title title="Delete this video" href="#" (click)="removeVideo($event)">
102 <span class="icon icon-delete"></span> <ng-container i18n>Delete</ng-container> 111 <span class="icon icon-delete"></span> <ng-container i18n>Delete</ng-container>
103 </a> 112 </a>
diff --git a/client/src/app/videos/+video-watch/video-watch.component.scss b/client/src/app/videos/+video-watch/video-watch.component.scss
index e63ab7bbd..1354de32e 100644
--- a/client/src/app/videos/+video-watch/video-watch.component.scss
+++ b/client/src/app/videos/+video-watch/video-watch.component.scss
@@ -1,6 +1,14 @@
1@import '_variables'; 1@import '_variables';
2@import '_mixins'; 2@import '_mixins';
3 3
4.root-row {
5 flex-direction: column;
6}
7
8.blacklisted-label {
9 font-weight: $font-semibold;
10}
11
4#video-element-wrapper { 12#video-element-wrapper {
5 background-color: #000; 13 background-color: #000;
6 display: flex; 14 display: flex;
@@ -259,6 +267,10 @@
259 background-image: url('../../../assets/images/video/blacklist.svg'); 267 background-image: url('../../../assets/images/video/blacklist.svg');
260 } 268 }
261 269
270 &.icon-unblacklist {
271 background-image: url('../../../assets/images/global/undo.svg');
272 }
273
262 &.icon-delete { 274 &.icon-delete {
263 background-image: url('../../../assets/images/global/delete-black.svg'); 275 background-image: url('../../../assets/images/global/delete-black.svg');
264 } 276 }
diff --git a/client/src/app/videos/+video-watch/video-watch.component.ts b/client/src/app/videos/+video-watch/video-watch.component.ts
index 878655d4a..bea13ec99 100644
--- a/client/src/app/videos/+video-watch/video-watch.component.ts
+++ b/client/src/app/videos/+video-watch/video-watch.component.ts
@@ -121,7 +121,8 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
121 this.videoCaptionService.listCaptions(uuid) 121 this.videoCaptionService.listCaptions(uuid)
122 ) 122 )
123 .pipe( 123 .pipe(
124 catchError(err => this.restExtractor.redirectTo404IfNotFound(err, [ 400, 404 ])) 124 // If 401, the video is private or blacklisted so redirect to 404
125 catchError(err => this.restExtractor.redirectTo404IfNotFound(err, [ 400, 401, 404 ]))
125 ) 126 )
126 .subscribe(([ video, captionsResult ]) => { 127 .subscribe(([ video, captionsResult ]) => {
127 const startTime = this.route.snapshot.queryParams.start 128 const startTime = this.route.snapshot.queryParams.start
@@ -217,6 +218,31 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
217 this.videoBlacklistModal.show() 218 this.videoBlacklistModal.show()
218 } 219 }
219 220
221 async unblacklistVideo (event: Event) {
222 event.preventDefault()
223
224 const confirmMessage = this.i18n(
225 'Do you really want to remove this video from the blacklist? It will be available again in the videos list.'
226 )
227
228 const res = await this.confirmService.confirm(confirmMessage, this.i18n('Unblacklist'))
229 if (res === false) return
230
231 this.videoBlacklistService.removeVideoFromBlacklist(this.video.id).subscribe(
232 () => {
233 this.notificationsService.success(
234 this.i18n('Success'),
235 this.i18n('Video {{name}} removed from the blacklist.', { name: this.video.name })
236 )
237
238 this.video.blacklisted = false
239 this.video.blacklistedReason = null
240 },
241
242 err => this.notificationsService.error(this.i18n('Error'), err.message)
243 )
244 }
245
220 isUserLoggedIn () { 246 isUserLoggedIn () {
221 return this.authService.isLoggedIn() 247 return this.authService.isLoggedIn()
222 } 248 }
@@ -229,6 +255,10 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
229 return this.video.isBlackistableBy(this.user) 255 return this.video.isBlackistableBy(this.user)
230 } 256 }
231 257
258 isVideoUnblacklistable () {
259 return this.video.isUnblacklistableBy(this.user)
260 }
261
232 getVideoPoster () { 262 getVideoPoster () {
233 if (!this.video) return '' 263 if (!this.video) return ''
234 264
diff --git a/client/src/assets/images/global/undo.svg b/client/src/assets/images/global/undo.svg
new file mode 100644
index 000000000..f1cca03f7
--- /dev/null
+++ b/client/src/assets/images/global/undo.svg
@@ -0,0 +1,11 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
3 <defs></defs>
4 <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
5 <g id="Artboard-4" transform="translate(-180.000000, -115.000000)" fill="#000">
6 <g id="4" transform="translate(180.000000, 115.000000)">
7 <path d="M10,19 C10.5522847,19 11,19.4477153 11,20 C11,20.5522847 10.5522847,21 10,21 C9.99404288,21 9.98809793,20.9999479 9.98216558,20.9998442 C5.01980239,20.990358 1,16.9646166 1,12 C1,7.02943725 5.02943725,3 10,3 C14.9705627,3 19,7.02943725 19,12 L17,12 C17,8.13400675 13.8659932,5 10,5 C6.13400675,5 3,8.13400675 3,12 C3,15.8659932 6.13400675,19 10,19 Z M14,12 L22,12 L18,16 L14,12 Z" id="Combined-Shape" transform="translate(11.500000, 12.000000) scale(-1, 1) translate(-11.500000, -12.000000) "></path>
8 </g>
9 </g>
10 </g>
11</svg>