diff options
author | Green-Star <Green-Star@users.noreply.github.com> | 2017-04-26 21:22:10 +0200 |
---|---|---|
committer | Bigard Florian <florian.bigard@gmail.com> | 2017-04-26 21:22:10 +0200 |
commit | 198b205c10dba362b9ae1ef6895b29d7e0dd685f (patch) | |
tree | 3be413139784f7445e775cbecccc6091a738360b /client | |
parent | 00871a261787ae1ed8446861ba2bd5eea9faca6d (diff) | |
download | PeerTube-198b205c10dba362b9ae1ef6895b29d7e0dd685f.tar.gz PeerTube-198b205c10dba362b9ae1ef6895b29d7e0dd685f.tar.zst PeerTube-198b205c10dba362b9ae1ef6895b29d7e0dd685f.zip |
Add ability for an administrator to remove any video (#61)
* Add ability for an admin to remove every video on the pod.
* Server: add BlacklistedVideos relation.
* Server: Insert in BlacklistedVideos relation upon deletion of a video.
* Server: Modify BlacklistedVideos schema to add Pod id information.
* Server: Moving insertion of a blacklisted video from the `afterDestroy` hook into the process of deletion of a video.
To avoid inserting a video when it is removed on its origin pod.
When a video is removed on its origin pod, the `afterDestroy` hook is fire, but no request is made on the delete('/:videoId') interface.
Hence, we insert into `BlacklistedVideos` only on request on delete('/:videoId') (if requirements for insertion are met).
* Server: Add removeVideoFromBlacklist hook on deletion of a video.
We are going to proceed in another way :).
We will add a new route : /:videoId/blacklist to blacklist a video.
We do not blacklist a video upon its deletion now (to distinguish a video blacklist from a regular video delete)
When we blacklist a video, the video remains in the DB, so we don't have any concern about its update. It just doesn't appear in the video list.
When we remove a video, we then have to remove it from the blacklist too.
We could also remove a video from the blacklist to 'unremove' it and make it appear again in the video list (will be another feature).
* Server: Add handler for new route post(/:videoId/blacklist)
* Client: Add isBlacklistable method
* Client: Update isRemovableBy method.
* Client: Move 'Delete video' feature from the video-list to the video-watch module.
* Server: Exclude blacklisted videos from the video list
* Server: Use findAll() in BlacklistedVideos.list() method
* Server: Fix addVideoToBlacklist function.
* Client: Add blacklist feature.
* Server: Use JavaScript Standard Style.
* Server: In checkUserCanDeleteVideo, move the callback call inside the db callback function
* Server: Modify BlacklistVideo relation
* Server: Modifiy Videos methods.
* Server: Add checkVideoIsBlacklistable method
* Server: Rewrite addVideoToBlacklist method
* Server: Fix checkVideoIsBlacklistable method
* Server: Add return to addVideoToBlacklist method
Diffstat (limited to 'client')
8 files changed, 71 insertions, 45 deletions
diff --git a/client/src/app/videos/shared/video.model.ts b/client/src/app/videos/shared/video.model.ts index 404e3bf45..1cfb312b6 100644 --- a/client/src/app/videos/shared/video.model.ts +++ b/client/src/app/videos/shared/video.model.ts | |||
@@ -85,8 +85,12 @@ export class Video { | |||
85 | this.by = Video.createByString(hash.author, hash.podHost); | 85 | this.by = Video.createByString(hash.author, hash.podHost); |
86 | } | 86 | } |
87 | 87 | ||
88 | isRemovableBy(user: User) { | 88 | isRemovableBy(user) { |
89 | return this.isLocal === true && user && this.author === user.username; | 89 | return user && this.isLocal === true && (this.author === user.username || user.isAdmin() === true); |
90 | } | ||
91 | |||
92 | isBlackistableBy(user) { | ||
93 | return user && user.isAdmin() === true && this.isLocal === false; | ||
90 | } | 94 | } |
91 | 95 | ||
92 | isVideoNSFWForUser(user: User) { | 96 | isVideoNSFWForUser(user: User) { |
diff --git a/client/src/app/videos/shared/video.service.ts b/client/src/app/videos/shared/video.service.ts index ee67bc1ae..a0965e20c 100644 --- a/client/src/app/videos/shared/video.service.ts +++ b/client/src/app/videos/shared/video.service.ts | |||
@@ -150,6 +150,12 @@ export class VideoService { | |||
150 | .catch((res) => this.restExtractor.handleError(res)); | 150 | .catch((res) => this.restExtractor.handleError(res)); |
151 | } | 151 | } |
152 | 152 | ||
153 | blacklistVideo(id: string) { | ||
154 | return this.authHttp.post(VideoService.BASE_VIDEO_URL + id + '/blacklist', {}) | ||
155 | .map(this.restExtractor.extractDataBool) | ||
156 | .catch((res) => this.restExtractor.handleError(res)); | ||
157 | } | ||
158 | |||
153 | private setVideoRate(id: string, rateType: RateType) { | 159 | private setVideoRate(id: string, rateType: RateType) { |
154 | const url = VideoService.BASE_VIDEO_URL + id + '/rate'; | 160 | const url = VideoService.BASE_VIDEO_URL + id + '/rate'; |
155 | const body = { | 161 | const body = { |
diff --git a/client/src/app/videos/video-list/video-list.component.ts b/client/src/app/videos/video-list/video-list.component.ts index ede1b51a9..b9f19b4f1 100644 --- a/client/src/app/videos/video-list/video-list.component.ts +++ b/client/src/app/videos/video-list/video-list.component.ts | |||
@@ -108,11 +108,6 @@ export class VideoListComponent implements OnInit, OnDestroy { | |||
108 | this.navigateToNewParams(); | 108 | this.navigateToNewParams(); |
109 | } | 109 | } |
110 | 110 | ||
111 | onRemoved(video: Video) { | ||
112 | this.notificationsService.success('Success', `Video ${video.name} deleted.`); | ||
113 | this.getVideos(); | ||
114 | } | ||
115 | |||
116 | onSort(sort: SortField) { | 111 | onSort(sort: SortField) { |
117 | this.sort = sort; | 112 | this.sort = sort; |
118 | 113 | ||
diff --git a/client/src/app/videos/video-list/video-miniature.component.html b/client/src/app/videos/video-list/video-miniature.component.html index 94b892698..b8b448631 100644 --- a/client/src/app/videos/video-list/video-miniature.component.html +++ b/client/src/app/videos/video-list/video-miniature.component.html | |||
@@ -10,10 +10,6 @@ | |||
10 | 10 | ||
11 | <span class="video-miniature-duration">{{ video.duration }}</span> | 11 | <span class="video-miniature-duration">{{ video.duration }}</span> |
12 | </a> | 12 | </a> |
13 | <span | ||
14 | *ngIf="displayRemoveIcon()" (click)="removeVideo(video.id)" | ||
15 | class="video-miniature-remove glyphicon glyphicon-remove" | ||
16 | ></span> | ||
17 | 13 | ||
18 | <div class="video-miniature-informations"> | 14 | <div class="video-miniature-informations"> |
19 | <span class="video-miniature-name-tags"> | 15 | <span class="video-miniature-name-tags"> |
diff --git a/client/src/app/videos/video-list/video-miniature.component.scss b/client/src/app/videos/video-list/video-miniature.component.scss index b8e90e8c5..f88ced819 100644 --- a/client/src/app/videos/video-list/video-miniature.component.scss +++ b/client/src/app/videos/video-list/video-miniature.component.scss | |||
@@ -42,20 +42,6 @@ | |||
42 | } | 42 | } |
43 | } | 43 | } |
44 | 44 | ||
45 | .video-miniature-remove { | ||
46 | display: inline-block; | ||
47 | position: absolute; | ||
48 | left: 16px; | ||
49 | background-color: rgba(0, 0, 0, 0.8); | ||
50 | color: rgba(255, 255, 255, 0.8); | ||
51 | padding: 2px; | ||
52 | cursor: pointer; | ||
53 | |||
54 | &:hover { | ||
55 | color: rgba(255, 255, 255, 0.9); | ||
56 | } | ||
57 | } | ||
58 | |||
59 | .video-miniature-informations { | 45 | .video-miniature-informations { |
60 | width: 200px; | 46 | width: 200px; |
61 | 47 | ||
diff --git a/client/src/app/videos/video-list/video-miniature.component.ts b/client/src/app/videos/video-list/video-miniature.component.ts index 888026dde..13deec381 100644 --- a/client/src/app/videos/video-list/video-miniature.component.ts +++ b/client/src/app/videos/video-list/video-miniature.component.ts | |||
@@ -13,8 +13,6 @@ import { User } from '../../shared'; | |||
13 | }) | 13 | }) |
14 | 14 | ||
15 | export class VideoMiniatureComponent { | 15 | export class VideoMiniatureComponent { |
16 | @Output() removed = new EventEmitter<any>(); | ||
17 | |||
18 | @Input() currentSort: SortField; | 16 | @Input() currentSort: SortField; |
19 | @Input() user: User; | 17 | @Input() user: User; |
20 | @Input() video: Video; | 18 | @Input() video: Video; |
@@ -28,10 +26,6 @@ export class VideoMiniatureComponent { | |||
28 | private videoService: VideoService | 26 | private videoService: VideoService |
29 | ) {} | 27 | ) {} |
30 | 28 | ||
31 | displayRemoveIcon() { | ||
32 | return this.hovering && this.video.isRemovableBy(this.user); | ||
33 | } | ||
34 | |||
35 | getVideoName() { | 29 | getVideoName() { |
36 | if (this.isVideoNSFWForThisUser()) | 30 | if (this.isVideoNSFWForThisUser()) |
37 | return 'NSFW'; | 31 | return 'NSFW'; |
@@ -47,20 +41,6 @@ export class VideoMiniatureComponent { | |||
47 | this.hovering = true; | 41 | this.hovering = true; |
48 | } | 42 | } |
49 | 43 | ||
50 | removeVideo(id: string) { | ||
51 | this.confirmService.confirm('Do you really want to delete this video?', 'Delete').subscribe( | ||
52 | res => { | ||
53 | if (res === false) return; | ||
54 | |||
55 | this.videoService.removeVideo(id).subscribe( | ||
56 | status => this.removed.emit(true), | ||
57 | |||
58 | error => this.notificationsService.error('Error', error.text) | ||
59 | ); | ||
60 | } | ||
61 | ); | ||
62 | } | ||
63 | |||
64 | isVideoNSFWForThisUser() { | 44 | isVideoNSFWForThisUser() { |
65 | return this.video.isVideoNSFWForUser(this.user); | 45 | return this.video.isVideoNSFWForUser(this.user); |
66 | } | 46 | } |
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 19e9bd9ed..ed26b513e 100644 --- a/client/src/app/videos/video-watch/video-watch.component.html +++ b/client/src/app/videos/video-watch/video-watch.component.html | |||
@@ -96,6 +96,18 @@ | |||
96 | <span class="glyphicon glyphicon-alert"></span> Report | 96 | <span class="glyphicon glyphicon-alert"></span> Report |
97 | </a> | 97 | </a> |
98 | </li> | 98 | </li> |
99 | |||
100 | <li *ngIf="isVideoRemovable()" role="menuitem"> | ||
101 | <a class="dropdown-item" title="Delete this video" href="#" (click)="removeVideo($event)"> | ||
102 | <span class="glyphicon glyphicon-remove"></span> Delete | ||
103 | </a> | ||
104 | </li> | ||
105 | |||
106 | <li *ngIf="isVideoBlacklistable()" role="menuitem"> | ||
107 | <a class="dropdown-item" title="Blacklist this video" href="#" (click)="blacklistVideo($event)"> | ||
108 | <span class="glyphicon glyphicon-eye-close"></span> Blacklist | ||
109 | </a> | ||
110 | </li> | ||
99 | </ul> | 111 | </ul> |
100 | </div> | 112 | </div> |
101 | </div> | 113 | </div> |
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 e04626a67..f582df45c 100644 --- a/client/src/app/videos/video-watch/video-watch.component.ts +++ b/client/src/app/videos/video-watch/video-watch.component.ts | |||
@@ -169,6 +169,45 @@ export class VideoWatchComponent implements OnInit, OnDestroy { | |||
169 | ); | 169 | ); |
170 | } | 170 | } |
171 | 171 | ||
172 | removeVideo(event: Event) { | ||
173 | event.preventDefault(); | ||
174 | this.confirmService.confirm('Do you really want to delete this video?', 'Delete').subscribe( | ||
175 | res => { | ||
176 | if (res === false) return; | ||
177 | |||
178 | this.videoService.removeVideo(this.video.id) | ||
179 | .subscribe( | ||
180 | status => { | ||
181 | this.notificationsService.success('Success', `Video ${this.video.name} deleted.`) | ||
182 | // Go back to the video-list. | ||
183 | this.router.navigate(['/videos/list']) | ||
184 | }, | ||
185 | |||
186 | error => this.notificationsService.error('Error', error.text) | ||
187 | ); | ||
188 | } | ||
189 | ); | ||
190 | } | ||
191 | |||
192 | blacklistVideo(event: Event) { | ||
193 | event.preventDefault() | ||
194 | this.confirmService.confirm('Do you really want to blacklist this video ?', 'Blacklist').subscribe( | ||
195 | res => { | ||
196 | if (res === false) return; | ||
197 | |||
198 | this.videoService.blacklistVideo(this.video.id) | ||
199 | .subscribe( | ||
200 | status => { | ||
201 | this.notificationsService.success('Success', `Video ${this.video.name} had been blacklisted.`) | ||
202 | this.router.navigate(['/videos/list']) | ||
203 | }, | ||
204 | |||
205 | error => this.notificationsService.error('Error', error.text) | ||
206 | ) | ||
207 | } | ||
208 | ) | ||
209 | } | ||
210 | |||
172 | showReportModal(event: Event) { | 211 | showReportModal(event: Event) { |
173 | event.preventDefault(); | 212 | event.preventDefault(); |
174 | this.videoReportModal.show(); | 213 | this.videoReportModal.show(); |
@@ -192,6 +231,14 @@ export class VideoWatchComponent implements OnInit, OnDestroy { | |||
192 | this.authService.getUser().username === this.video.author; | 231 | this.authService.getUser().username === this.video.author; |
193 | } | 232 | } |
194 | 233 | ||
234 | isVideoRemovable() { | ||
235 | return this.video.isRemovableBy(this.authService.getUser()); | ||
236 | } | ||
237 | |||
238 | isVideoBlacklistable() { | ||
239 | return this.video.isBlackistableBy(this.authService.getUser()); | ||
240 | } | ||
241 | |||
195 | private checkUserRating() { | 242 | private checkUserRating() { |
196 | // Unlogged users do not have ratings | 243 | // Unlogged users do not have ratings |
197 | if (this.isUserLoggedIn() === false) return; | 244 | if (this.isUserLoggedIn() === false) return; |