aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorChocobozzz <me@florianbigard.com>2018-02-22 15:29:32 +0100
committerChocobozzz <me@florianbigard.com>2018-02-22 15:29:32 +0100
commit1f30a1853e38c20a45722dbd6d38aaaec63839e8 (patch)
treecdce8cc9fcc62d9b3343c9478b1dbcefedcea972
parent78967fca4cacbc247fa6fb62d64b2d6825a10804 (diff)
downloadPeerTube-1f30a1853e38c20a45722dbd6d38aaaec63839e8.tar.gz
PeerTube-1f30a1853e38c20a45722dbd6d38aaaec63839e8.tar.zst
PeerTube-1f30a1853e38c20a45722dbd6d38aaaec63839e8.zip
Add confirm when admin use custom js/css
-rw-r--r--client/.angular-cli.json7
-rw-r--r--client/src/app/+admin/config/config.routes.ts2
-rw-r--r--client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.ts32
-rw-r--r--client/src/app/+admin/follows/following-add/following-add.component.ts21
-rw-r--r--client/src/app/+admin/follows/following-list/following-list.component.ts21
-rw-r--r--client/src/app/+admin/users/user-list/user-list.component.ts25
-rw-r--r--client/src/app/+admin/video-blacklist/video-blacklist-list/video-blacklist-list.component.ts21
-rw-r--r--client/src/app/account/account-videos/account-videos.component.ts80
-rw-r--r--client/src/app/core/confirm/confirm.component.html7
-rw-r--r--client/src/app/core/confirm/confirm.component.scss17
-rw-r--r--client/src/app/core/confirm/confirm.component.ts23
-rw-r--r--client/src/app/core/confirm/confirm.service.ts13
-rw-r--r--client/src/app/core/core.module.ts2
-rw-r--r--client/src/app/shared/guards/can-deactivate-guard.service.ts2
-rw-r--r--client/src/app/videos/+video-watch/comment/video-comments.component.ts55
-rw-r--r--client/src/app/videos/+video-watch/video-watch.component.ts51
-rw-r--r--server/controllers/client.ts2
17 files changed, 208 insertions, 173 deletions
diff --git a/client/.angular-cli.json b/client/.angular-cli.json
index 643e1b319..cb387db0a 100644
--- a/client/.angular-cli.json
+++ b/client/.angular-cli.json
@@ -8,12 +8,7 @@
8 "root": "src", 8 "root": "src",
9 "outDir": "dist", 9 "outDir": "dist",
10 "assets": [ 10 "assets": [
11 { 11 "./assets/images",
12 "glob": "**/*",
13 "input": "./assets/images",
14 "output": "./client/assets/images",
15 "allowOutsideOutDir": false
16 },
17 "./manifest.json" 12 "./manifest.json"
18 ], 13 ],
19 "deployUrl": "client/", 14 "deployUrl": "client/",
diff --git a/client/src/app/+admin/config/config.routes.ts b/client/src/app/+admin/config/config.routes.ts
index a46b0ddfd..2ca2f8fde 100644
--- a/client/src/app/+admin/config/config.routes.ts
+++ b/client/src/app/+admin/config/config.routes.ts
@@ -23,7 +23,7 @@ export const ConfigRoutes: Routes = [
23 component: EditCustomConfigComponent, 23 component: EditCustomConfigComponent,
24 data: { 24 data: {
25 meta: { 25 meta: {
26 title: 'Following list' 26 title: 'Edit custom configuration'
27 } 27 }
28 } 28 }
29 } 29 }
diff --git a/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.ts b/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.ts
index 027268536..ccec89a8e 100644
--- a/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.ts
+++ b/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.ts
@@ -2,6 +2,7 @@ import { Component, OnInit } from '@angular/core'
2import { FormBuilder, FormGroup } from '@angular/forms' 2import { FormBuilder, FormGroup } from '@angular/forms'
3import { Router } from '@angular/router' 3import { Router } from '@angular/router'
4import { ConfigService } from '@app/+admin/config/shared/config.service' 4import { ConfigService } from '@app/+admin/config/shared/config.service'
5import { ConfirmService } from '@app/core'
5import { ServerService } from '@app/core/server/server.service' 6import { ServerService } from '@app/core/server/server.service'
6import { FormReactive, USER_VIDEO_QUOTA } from '@app/shared' 7import { FormReactive, USER_VIDEO_QUOTA } from '@app/shared'
7import { 8import {
@@ -61,12 +62,16 @@ export class EditCustomConfigComponent extends FormReactive implements OnInit {
61 userVideoQuota: USER_VIDEO_QUOTA.MESSAGES 62 userVideoQuota: USER_VIDEO_QUOTA.MESSAGES
62 } 63 }
63 64
65 private oldCustomJavascript: string
66 private oldCustomCSS: string
67
64 constructor ( 68 constructor (
65 private formBuilder: FormBuilder, 69 private formBuilder: FormBuilder,
66 private router: Router, 70 private router: Router,
67 private notificationsService: NotificationsService, 71 private notificationsService: NotificationsService,
68 private configService: ConfigService, 72 private configService: ConfigService,
69 private serverService: ServerService 73 private serverService: ServerService,
74 private confirmService: ConfirmService
70 ) { 75 ) {
71 super() 76 super()
72 } 77 }
@@ -109,6 +114,9 @@ export class EditCustomConfigComponent extends FormReactive implements OnInit {
109 res => { 114 res => {
110 this.customConfig = res 115 this.customConfig = res
111 116
117 this.oldCustomCSS = this.customConfig.instance.customizations.css
118 this.oldCustomJavascript = this.customConfig.instance.customizations.javascript
119
112 this.updateForm() 120 this.updateForm()
113 }, 121 },
114 122
@@ -124,7 +132,27 @@ export class EditCustomConfigComponent extends FormReactive implements OnInit {
124 return this.form.value['signupEnabled'] === true 132 return this.form.value['signupEnabled'] === true
125 } 133 }
126 134
127 formValidated () { 135 async formValidated () {
136 const newCustomizationJavascript = this.form.value['customizationJavascript']
137 const newCustomizationCSS = this.form.value['customizationCSS']
138
139 const customizations = []
140 if (newCustomizationJavascript && newCustomizationJavascript !== this.oldCustomJavascript) customizations.push('JavaScript')
141 if (newCustomizationCSS && newCustomizationCSS !== this.oldCustomCSS) customizations.push('CSS')
142
143 if (customizations.length !== 0) {
144 const customizationsText = customizations.join('/')
145
146 const message = `You set custom ${customizationsText}. ` +
147 'This could lead to security issues or bugs if you do not understand it. ' +
148 'Are you sure you want to update the configuration?'
149 const label = `Please type "I understand the ${customizationsText} I set" to confirm.`
150 const expectedInputValue = `I understand the ${customizationsText} I set`
151
152 const confirmRes = await this.confirmService.confirmWithInput(message, label, expectedInputValue)
153 if (confirmRes === false) return
154 }
155
128 const data = { 156 const data = {
129 instance: { 157 instance: {
130 name: this.form.value['instanceName'], 158 name: this.form.value['instanceName'],
diff --git a/client/src/app/+admin/follows/following-add/following-add.component.ts b/client/src/app/+admin/follows/following-add/following-add.component.ts
index bf842129d..c296c8852 100644
--- a/client/src/app/+admin/follows/following-add/following-add.component.ts
+++ b/client/src/app/+admin/follows/following-add/following-add.component.ts
@@ -43,7 +43,7 @@ export class FollowingAddComponent {
43 } 43 }
44 } 44 }
45 45
46 addFollowing () { 46 async addFollowing () {
47 this.error = '' 47 this.error = ''
48 48
49 const hosts = this.getNotEmptyHosts() 49 const hosts = this.getNotEmptyHosts()
@@ -57,20 +57,17 @@ export class FollowingAddComponent {
57 } 57 }
58 58
59 const confirmMessage = 'If you confirm, you will send a follow request to:<br /> - ' + hosts.join('<br /> - ') 59 const confirmMessage = 'If you confirm, you will send a follow request to:<br /> - ' + hosts.join('<br /> - ')
60 this.confirmService.confirm(confirmMessage, 'Follow new server(s)').subscribe( 60 const res = await this.confirmService.confirm(confirmMessage, 'Follow new server(s)')
61 res => { 61 if (res === false) return
62 if (res === false) return
63 62
64 this.followService.follow(hosts).subscribe( 63 this.followService.follow(hosts).subscribe(
65 status => { 64 () => {
66 this.notificationsService.success('Success', 'Follow request(s) sent!') 65 this.notificationsService.success('Success', 'Follow request(s) sent!')
67 66
68 setTimeout(() => this.router.navigate([ '/admin/follows/following-list' ]), 500) 67 setTimeout(() => this.router.navigate([ '/admin/follows/following-list' ]), 500)
69 }, 68 },
70 69
71 err => this.notificationsService.error('Error', err.message) 70 err => this.notificationsService.error('Error', err.message)
72 )
73 }
74 ) 71 )
75 } 72 }
76 73
diff --git a/client/src/app/+admin/follows/following-list/following-list.component.ts b/client/src/app/+admin/follows/following-list/following-list.component.ts
index d4f8d0309..ad1bd4536 100644
--- a/client/src/app/+admin/follows/following-list/following-list.component.ts
+++ b/client/src/app/+admin/follows/following-list/following-list.component.ts
@@ -25,20 +25,17 @@ export class FollowingListComponent extends RestTable {
25 super() 25 super()
26 } 26 }
27 27
28 removeFollowing (follow: AccountFollow) { 28 async removeFollowing (follow: AccountFollow) {
29 this.confirmService.confirm(`Do you really want to unfollow ${follow.following.host}?`, 'Unfollow').subscribe( 29 const res = await this.confirmService.confirm(`Do you really want to unfollow ${follow.following.host}?`, 'Unfollow')
30 res => { 30 if (res === false) return
31 if (res === false) return
32 31
33 this.followService.unfollow(follow).subscribe( 32 this.followService.unfollow(follow).subscribe(
34 () => { 33 () => {
35 this.notificationsService.success('Success', `You are not following ${follow.following.host} anymore.`) 34 this.notificationsService.success('Success', `You are not following ${follow.following.host} anymore.`)
36 this.loadData() 35 this.loadData()
37 }, 36 },
38 37
39 err => this.notificationsService.error('Error', err.message) 38 err => this.notificationsService.error('Error', err.message)
40 )
41 }
42 ) 39 )
43 } 40 }
44 41
diff --git a/client/src/app/+admin/users/user-list/user-list.component.ts b/client/src/app/+admin/users/user-list/user-list.component.ts
index 1e8e1af49..512152808 100644
--- a/client/src/app/+admin/users/user-list/user-list.component.ts
+++ b/client/src/app/+admin/users/user-list/user-list.component.ts
@@ -1,10 +1,10 @@
1import { Component } from '@angular/core' 1import { Component } from '@angular/core'
2import { SortMeta } from 'primeng/components/common/sortmeta'
3 2
4import { NotificationsService } from 'angular2-notifications' 3import { NotificationsService } from 'angular2-notifications'
4import { SortMeta } from 'primeng/components/common/sortmeta'
5 5
6import { ConfirmService } from '../../../core' 6import { ConfirmService } from '../../../core'
7import { RestTable, RestPagination, User } from '../../../shared' 7import { RestPagination, RestTable, User } from '../../../shared'
8import { UserService } from '../shared' 8import { UserService } from '../shared'
9 9
10@Component({ 10@Component({
@@ -27,25 +27,22 @@ export class UserListComponent extends RestTable {
27 super() 27 super()
28 } 28 }
29 29
30 removeUser (user: User) { 30 async removeUser (user: User) {
31 if (user.username === 'root') { 31 if (user.username === 'root') {
32 this.notificationsService.error('Error', 'You cannot delete root.') 32 this.notificationsService.error('Error', 'You cannot delete root.')
33 return 33 return
34 } 34 }
35 35
36 this.confirmService.confirm('Do you really want to delete this user?', 'Delete').subscribe( 36 const res = await this.confirmService.confirm('Do you really want to delete this user?', 'Delete')
37 res => { 37 if (res === false) return
38 if (res === false) return
39 38
40 this.userService.removeUser(user).subscribe( 39 this.userService.removeUser(user).subscribe(
41 () => { 40 () => {
42 this.notificationsService.success('Success', `User ${user.username} deleted.`) 41 this.notificationsService.success('Success', `User ${user.username} deleted.`)
43 this.loadData() 42 this.loadData()
44 }, 43 },
45 44
46 err => this.notificationsService.error('Error', err.message) 45 err => this.notificationsService.error('Error', err.message)
47 )
48 }
49 ) 46 )
50 } 47 }
51 48
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 56024b247..f4cf21259 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
@@ -31,22 +31,19 @@ export class VideoBlacklistListComponent extends RestTable implements OnInit {
31 this.loadData() 31 this.loadData()
32 } 32 }
33 33
34 removeVideoFromBlacklist (entry: BlacklistedVideo) { 34 async removeVideoFromBlacklist (entry: BlacklistedVideo) {
35 const confirmMessage = 'Do you really want to remove this video from the blacklist ? It will be available again in the video list.' 35 const confirmMessage = 'Do you really want to remove this video from the blacklist ? It will be available again in the video list.'
36 36
37 this.confirmService.confirm(confirmMessage, 'Remove').subscribe( 37 const res = await this.confirmService.confirm(confirmMessage, 'Remove')
38 res => { 38 if (res === false) return
39 if (res === false) return
40 39
41 this.videoBlacklistService.removeVideoFromBlacklist(entry.videoId).subscribe( 40 this.videoBlacklistService.removeVideoFromBlacklist(entry.videoId).subscribe(
42 status => { 41 () => {
43 this.notificationsService.success('Success', `Video ${entry.name} removed from the blacklist.`) 42 this.notificationsService.success('Success', `Video ${entry.name} removed from the blacklist.`)
44 this.loadData() 43 this.loadData()
45 }, 44 },
46 45
47 err => this.notificationsService.error('Error', err.message) 46 err => this.notificationsService.error('Error', err.message)
48 )
49 }
50 ) 47 )
51 } 48 }
52 49
diff --git a/client/src/app/account/account-videos/account-videos.component.ts b/client/src/app/account/account-videos/account-videos.component.ts
index e9d044dbf..a286bad1c 100644
--- a/client/src/app/account/account-videos/account-videos.component.ts
+++ b/client/src/app/account/account-videos/account-videos.component.ts
@@ -56,55 +56,49 @@ export class AccountVideosComponent extends AbstractVideoList implements OnInit
56 return this.videoService.getMyVideos(newPagination, this.sort) 56 return this.videoService.getMyVideos(newPagination, this.sort)
57 } 57 }
58 58
59 deleteSelectedVideos () { 59 async deleteSelectedVideos () {
60 const toDeleteVideosIds = Object.keys(this.checkedVideos) 60 const toDeleteVideosIds = Object.keys(this.checkedVideos)
61 .filter(k => this.checkedVideos[k] === true) 61 .filter(k => this.checkedVideos[k] === true)
62 .map(k => parseInt(k, 10)) 62 .map(k => parseInt(k, 10))
63 63
64 this.confirmService.confirm(`Do you really want to delete ${toDeleteVideosIds.length} videos?`, 'Delete').subscribe( 64 const res = await this.confirmService.confirm(`Do you really want to delete ${toDeleteVideosIds.length} videos?`, 'Delete')
65 res => { 65 if (res === false) return
66 if (res === false) return 66
67 67 const observables: Observable<any>[] = []
68 const observables: Observable<any>[] = [] 68 for (const videoId of toDeleteVideosIds) {
69 for (const videoId of toDeleteVideosIds) { 69 const o = this.videoService
70 const o = this.videoService 70 .removeVideo(videoId)
71 .removeVideo(videoId) 71 .do(() => this.spliceVideosById(videoId))
72 .do(() => this.spliceVideosById(videoId)) 72
73 73 observables.push(o)
74 observables.push(o) 74 }
75 } 75
76 76 Observable.from(observables)
77 Observable.from(observables) 77 .concatAll()
78 .concatAll() 78 .subscribe(
79 .subscribe( 79 res => {
80 res => { 80 this.notificationsService.success('Success', `${toDeleteVideosIds.length} videos deleted.`)
81 this.notificationsService.success('Success', `${toDeleteVideosIds.length} videos deleted.`) 81 this.buildVideoPages()
82 this.buildVideoPages() 82 },
83 }, 83
84 84 err => this.notificationsService.error('Error', err.message)
85 err => this.notificationsService.error('Error', err.message) 85 )
86 )
87 }
88 )
89 } 86 }
90 87
91 deleteVideo (video: Video) { 88 async deleteVideo (video: Video) {
92 this.confirmService.confirm(`Do you really want to delete ${video.name}?`, 'Delete').subscribe( 89 const res = await this.confirmService.confirm(`Do you really want to delete ${video.name}?`, 'Delete')
93 res => { 90 if (res === false) return
94 if (res === false) return 91
95 92 this.videoService.removeVideo(video.id)
96 this.videoService.removeVideo(video.id) 93 .subscribe(
97 .subscribe( 94 status => {
98 status => { 95 this.notificationsService.success('Success', `Video ${video.name} deleted.`)
99 this.notificationsService.success('Success', `Video ${video.name} deleted.`) 96 this.spliceVideosById(video.id)
100 this.spliceVideosById(video.id) 97 this.buildVideoPages()
101 this.buildVideoPages() 98 },
102 }, 99
103 100 error => this.notificationsService.error('Error', error.message)
104 error => this.notificationsService.error('Error', error.message) 101 )
105 )
106 }
107 )
108 } 102 }
109 103
110 private spliceVideosById (id: number) { 104 private spliceVideosById (id: number) {
diff --git a/client/src/app/core/confirm/confirm.component.html b/client/src/app/core/confirm/confirm.component.html
index cc2c28de2..90274b248 100644
--- a/client/src/app/core/confirm/confirm.component.html
+++ b/client/src/app/core/confirm/confirm.component.html
@@ -10,13 +10,18 @@
10 <div class="modal-body" > 10 <div class="modal-body" >
11 <div [innerHtml]="message"></div> 11 <div [innerHtml]="message"></div>
12 12
13 <div *ngIf="inputLabel && expectedInputValue" class="form-group">
14 <label for="confirmInput">{{ inputLabel }}</label>
15 <input type="text" id="confirmInput" name="confirmInput" [(ngModel)]="inputValue" />
16 </div>
17
13 <div class="form-group inputs"> 18 <div class="form-group inputs">
14 <span class="action-button action-button-cancel" (click)="cancel()"> 19 <span class="action-button action-button-cancel" (click)="cancel()">
15 Cancel 20 Cancel
16 </span> 21 </span>
17 22
18 <input 23 <input
19 type="submit" value="Confirm" class="action-button-submit" 24 type="submit" value="Confirm" class="action-button-submit" [disabled]="isConfirmationDisabled()"
20 (click)="confirm()" 25 (click)="confirm()"
21 > 26 >
22 </div> 27 </div>
diff --git a/client/src/app/core/confirm/confirm.component.scss b/client/src/app/core/confirm/confirm.component.scss
new file mode 100644
index 000000000..93dd7926b
--- /dev/null
+++ b/client/src/app/core/confirm/confirm.component.scss
@@ -0,0 +1,17 @@
1@import '_variables';
2@import '_mixins';
3
4.button {
5 padding: 0 13px;
6}
7
8input[type=text] {
9 @include peertube-input-text(100%);
10 display: block;
11}
12
13.form-group {
14 margin: 20px 0;
15}
16
17
diff --git a/client/src/app/core/confirm/confirm.component.ts b/client/src/app/core/confirm/confirm.component.ts
index 0515d969a..8f81b7a98 100644
--- a/client/src/app/core/confirm/confirm.component.ts
+++ b/client/src/app/core/confirm/confirm.component.ts
@@ -4,21 +4,20 @@ import { ModalDirective } from 'ngx-bootstrap/modal'
4 4
5import { ConfirmService } from './confirm.service' 5import { ConfirmService } from './confirm.service'
6 6
7export interface ConfigChangedEvent {
8 columns: { [id: string]: { isDisplayed: boolean } }
9 config: { resultsPerPage: number }
10}
11
12@Component({ 7@Component({
13 selector: 'my-confirm', 8 selector: 'my-confirm',
14 templateUrl: './confirm.component.html', 9 templateUrl: './confirm.component.html',
15 styles: [ '.button { padding: 0 13px; }' ] 10 styleUrls: [ './confirm.component.scss' ]
16}) 11})
17export class ConfirmComponent implements OnInit { 12export class ConfirmComponent implements OnInit {
18 @ViewChild('confirmModal') confirmModal: ModalDirective 13 @ViewChild('confirmModal') confirmModal: ModalDirective
19 14
20 title = '' 15 title = ''
21 message = '' 16 message = ''
17 expectedInputValue = ''
18 inputLabel = ''
19
20 inputValue = ''
22 21
23 constructor (private confirmService: ConfirmService) { 22 constructor (private confirmService: ConfirmService) {
24 // Empty 23 // Empty
@@ -31,10 +30,13 @@ export class ConfirmComponent implements OnInit {
31 } 30 }
32 31
33 this.confirmService.showConfirm.subscribe( 32 this.confirmService.showConfirm.subscribe(
34 ({ title, message }) => { 33 ({ title, message, expectedInputValue, inputLabel }) => {
35 this.title = title 34 this.title = title
36 this.message = message 35 this.message = message
37 36
37 this.inputLabel = inputLabel
38 this.expectedInputValue = expectedInputValue
39
38 this.showModal() 40 this.showModal()
39 } 41 }
40 ) 42 )
@@ -52,6 +54,13 @@ export class ConfirmComponent implements OnInit {
52 this.hideModal() 54 this.hideModal()
53 } 55 }
54 56
57 isConfirmationDisabled () {
58 // No input validation
59 if (!this.inputLabel || !this.expectedInputValue) return false
60
61 return this.expectedInputValue !== this.inputValue
62 }
63
55 showModal () { 64 showModal () {
56 this.confirmModal.show() 65 this.confirmModal.show()
57 } 66 }
diff --git a/client/src/app/core/confirm/confirm.service.ts b/client/src/app/core/confirm/confirm.service.ts
index f12ff1848..f30feb9d0 100644
--- a/client/src/app/core/confirm/confirm.service.ts
+++ b/client/src/app/core/confirm/confirm.service.ts
@@ -1,15 +1,22 @@
1import { Injectable } from '@angular/core' 1import { Injectable } from '@angular/core'
2import { Subject } from 'rxjs/Subject' 2import { Subject } from 'rxjs/Subject'
3import 'rxjs/add/operator/first' 3import 'rxjs/add/operator/first'
4import 'rxjs/add/operator/toPromise'
4 5
5@Injectable() 6@Injectable()
6export class ConfirmService { 7export class ConfirmService {
7 showConfirm = new Subject<{ title, message }>() 8 showConfirm = new Subject<{ title: string, message: string, inputLabel?: string, expectedInputValue?: string }>()
8 confirmResponse = new Subject<boolean>() 9 confirmResponse = new Subject<boolean>()
9 10
10 confirm (message = '', title = '') { 11 confirm (message: string, title = '') {
11 this.showConfirm.next({ title, message }) 12 this.showConfirm.next({ title, message })
12 13
13 return this.confirmResponse.asObservable().first() 14 return this.confirmResponse.asObservable().first().toPromise()
15 }
16
17 confirmWithInput (message: string, inputLabel: string, expectedInputValue: string, title = '') {
18 this.showConfirm.next({ title, message, inputLabel, expectedInputValue })
19
20 return this.confirmResponse.asObservable().first().toPromise()
14 } 21 }
15} 22}
diff --git a/client/src/app/core/core.module.ts b/client/src/app/core/core.module.ts
index eea6f340b..36dbe8b5c 100644
--- a/client/src/app/core/core.module.ts
+++ b/client/src/app/core/core.module.ts
@@ -1,5 +1,6 @@
1import { CommonModule } from '@angular/common' 1import { CommonModule } from '@angular/common'
2import { NgModule, Optional, SkipSelf } from '@angular/core' 2import { NgModule, Optional, SkipSelf } from '@angular/core'
3import { FormsModule } from '@angular/forms'
3import { BrowserAnimationsModule } from '@angular/platform-browser/animations' 4import { BrowserAnimationsModule } from '@angular/platform-browser/animations'
4import { RouterModule } from '@angular/router' 5import { RouterModule } from '@angular/router'
5import { LoadingBarModule } from '@ngx-loading-bar/core' 6import { LoadingBarModule } from '@ngx-loading-bar/core'
@@ -18,6 +19,7 @@ import { ServerService } from './server'
18 imports: [ 19 imports: [
19 CommonModule, 20 CommonModule,
20 RouterModule, 21 RouterModule,
22 FormsModule,
21 BrowserAnimationsModule, 23 BrowserAnimationsModule,
22 24
23 ModalModule, 25 ModalModule,
diff --git a/client/src/app/shared/guards/can-deactivate-guard.service.ts b/client/src/app/shared/guards/can-deactivate-guard.service.ts
index 15618f699..c3b5f37f8 100644
--- a/client/src/app/shared/guards/can-deactivate-guard.service.ts
+++ b/client/src/app/shared/guards/can-deactivate-guard.service.ts
@@ -15,7 +15,7 @@ export class CanDeactivateGuard implements CanDeactivate<CanComponentDeactivate>
15 currentRoute: ActivatedRouteSnapshot, 15 currentRoute: ActivatedRouteSnapshot,
16 currentState: RouterStateSnapshot, 16 currentState: RouterStateSnapshot,
17 nextState: RouterStateSnapshot 17 nextState: RouterStateSnapshot
18 ): Observable<boolean> | boolean { 18 ) {
19 const result = component.canDeactivate() 19 const result = component.canDeactivate()
20 const text = result.text || 'All unsaved data will be lost, are you sure you want to leave this page?' 20 const text = result.text || 'All unsaved data will be lost, are you sure you want to leave this page?'
21 21
diff --git a/client/src/app/videos/+video-watch/comment/video-comments.component.ts b/client/src/app/videos/+video-watch/comment/video-comments.component.ts
index 16f1a0643..711a01ba0 100644
--- a/client/src/app/videos/+video-watch/comment/video-comments.component.ts
+++ b/client/src/app/videos/+video-watch/comment/video-comments.component.ts
@@ -109,38 +109,35 @@ export class VideoCommentsComponent implements OnInit, OnChanges, OnDestroy {
109 this.viewReplies(commentTree.comment.id) 109 this.viewReplies(commentTree.comment.id)
110 } 110 }
111 111
112 onWantedToDelete (commentToDelete: VideoComment) { 112 async onWantedToDelete (commentToDelete: VideoComment) {
113 let message = 'Do you really want to delete this comment?' 113 let message = 'Do you really want to delete this comment?'
114 if (commentToDelete.totalReplies !== 0) message += `${commentToDelete.totalReplies} would be deleted too.` 114 if (commentToDelete.totalReplies !== 0) message += `${commentToDelete.totalReplies} would be deleted too.`
115 115
116 this.confirmService.confirm(message, 'Delete').subscribe( 116 const res = await this.confirmService.confirm(message, 'Delete')
117 res => { 117 if (res === false) return
118 if (res === false) return 118
119 119 this.videoCommentService.deleteVideoComment(commentToDelete.videoId, commentToDelete.id)
120 this.videoCommentService.deleteVideoComment(commentToDelete.videoId, commentToDelete.id) 120 .subscribe(
121 .subscribe( 121 () => {
122 () => { 122 // Delete the comment in the tree
123 // Delete the comment in the tree 123 if (commentToDelete.inReplyToCommentId) {
124 if (commentToDelete.inReplyToCommentId) { 124 const thread = this.threadComments[commentToDelete.threadId]
125 const thread = this.threadComments[commentToDelete.threadId] 125 if (!thread) {
126 if (!thread) { 126 console.error(`Cannot find thread ${commentToDelete.threadId} of the comment to delete ${commentToDelete.id}`)
127 console.error(`Cannot find thread ${commentToDelete.threadId} of the comment to delete ${commentToDelete.id}`) 127 return
128 return 128 }
129 } 129
130 130 this.deleteLocalCommentThread(thread, commentToDelete)
131 this.deleteLocalCommentThread(thread, commentToDelete) 131 return
132 return 132 }
133 } 133
134 134 // Delete the thread
135 // Delete the thread 135 this.comments = this.comments.filter(c => c.id !== commentToDelete.id)
136 this.comments = this.comments.filter(c => c.id !== commentToDelete.id) 136 this.componentPagination.totalItems--
137 this.componentPagination.totalItems-- 137 },
138 }, 138
139 139 err => this.notificationsService.error('Error', err.message)
140 err => this.notificationsService.error('Error', err.message) 140 )
141 )
142 }
143 )
144 } 141 }
145 142
146 isUserLoggedIn () { 143 isUserLoggedIn () {
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 6b118b1de..d04d50310 100644
--- a/client/src/app/videos/+video-watch/video-watch.component.ts
+++ b/client/src/app/videos/+video-watch/video-watch.component.ts
@@ -130,24 +130,21 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
130 } 130 }
131 } 131 }
132 132
133 blacklistVideo (event: Event) { 133 async blacklistVideo (event: Event) {
134 event.preventDefault() 134 event.preventDefault()
135 135
136 this.confirmService.confirm('Do you really want to blacklist this video?', 'Blacklist').subscribe( 136 const res = await this.confirmService.confirm('Do you really want to blacklist this video?', 'Blacklist')
137 res => { 137 if (res === false) return
138 if (res === false) return
139 138
140 this.videoBlacklistService.blacklistVideo(this.video.id) 139 this.videoBlacklistService.blacklistVideo(this.video.id)
141 .subscribe( 140 .subscribe(
142 status => { 141 status => {
143 this.notificationsService.success('Success', `Video ${this.video.name} had been blacklisted.`) 142 this.notificationsService.success('Success', `Video ${this.video.name} had been blacklisted.`)
144 this.router.navigate(['/videos/list']) 143 this.router.navigate(['/videos/list'])
145 }, 144 },
146 145
147 error => this.notificationsService.error('Error', error.message) 146 error => this.notificationsService.error('Error', error.message)
148 ) 147 )
149 }
150 )
151 } 148 }
152 149
153 showMoreDescription () { 150 showMoreDescription () {
@@ -236,26 +233,22 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
236 return this.video.isRemovableBy(this.authService.getUser()) 233 return this.video.isRemovableBy(this.authService.getUser())
237 } 234 }
238 235
239 removeVideo (event: Event) { 236 async removeVideo (event: Event) {
240 event.preventDefault() 237 event.preventDefault()
241 238
242 this.confirmService.confirm('Do you really want to delete this video?', 'Delete') 239 const res = await this.confirmService.confirm('Do you really want to delete this video?', 'Delete')
243 .subscribe( 240 if (res === false) return
244 res => {
245 if (res === false) return
246 241
247 this.videoService.removeVideo(this.video.id) 242 this.videoService.removeVideo(this.video.id)
248 .subscribe( 243 .subscribe(
249 status => { 244 status => {
250 this.notificationsService.success('Success', `Video ${this.video.name} deleted.`) 245 this.notificationsService.success('Success', `Video ${this.video.name} deleted.`)
251 246
252 // Go back to the video-list. 247 // Go back to the video-list.
253 this.router.navigate([ '/videos/list' ]) 248 this.router.navigate([ '/videos/list' ])
254 }, 249 },
255 250
256 error => this.notificationsService.error('Error', error.message) 251 error => this.notificationsService.error('Error', error.message)
257 )
258 }
259 ) 252 )
260 } 253 }
261 254
diff --git a/server/controllers/client.ts b/server/controllers/client.ts
index 2fcca6f76..df2eee9c9 100644
--- a/server/controllers/client.ts
+++ b/server/controllers/client.ts
@@ -10,7 +10,7 @@ import { VideoModel } from '../models/video/video'
10const clientsRouter = express.Router() 10const clientsRouter = express.Router()
11 11
12const distPath = join(root(), 'client', 'dist') 12const distPath = join(root(), 'client', 'dist')
13const assetsImagesPath = join(root(), 'client', 'dist', 'client', 'assets', 'images') 13const assetsImagesPath = join(root(), 'client', 'dist', 'assets', 'images')
14const embedPath = join(distPath, 'standalone', 'videos', 'embed.html') 14const embedPath = join(distPath, 'standalone', 'videos', 'embed.html')
15const indexPath = join(distPath, 'index.html') 15const indexPath = join(distPath, 'index.html')
16 16