aboutsummaryrefslogtreecommitdiffhomepage
path: root/client/src/app/+admin
diff options
context:
space:
mode:
authorChocobozzz <me@florianbigard.com>2018-08-09 17:51:25 +0200
committerChocobozzz <me@florianbigard.com>2018-08-09 17:55:05 +0200
commit141b177db088891e84040f68aa95008fb52f1d44 (patch)
tree20168077514c920f5b4da4696707db55f51b158d /client/src/app/+admin
parent63347a0ff966c7863e5b7431effa1cb0668df893 (diff)
downloadPeerTube-141b177db088891e84040f68aa95008fb52f1d44.tar.gz
PeerTube-141b177db088891e84040f68aa95008fb52f1d44.tar.zst
PeerTube-141b177db088891e84040f68aa95008fb52f1d44.zip
Add ability to ban/unban users
Diffstat (limited to 'client/src/app/+admin')
-rw-r--r--client/src/app/+admin/admin.module.ts2
-rw-r--r--client/src/app/+admin/users/shared/user.service.ts12
-rw-r--r--client/src/app/+admin/users/user-list/user-ban-modal.component.html32
-rw-r--r--client/src/app/+admin/users/user-list/user-ban-modal.component.scss6
-rw-r--r--client/src/app/+admin/users/user-list/user-ban-modal.component.ts68
-rw-r--r--client/src/app/+admin/users/user-list/user-list.component.html33
-rw-r--r--client/src/app/+admin/users/user-list/user-list.component.scss18
-rw-r--r--client/src/app/+admin/users/user-list/user-list.component.ts60
8 files changed, 221 insertions, 10 deletions
diff --git a/client/src/app/+admin/admin.module.ts b/client/src/app/+admin/admin.module.ts
index d7ae2f7f0..04b7ec5c1 100644
--- a/client/src/app/+admin/admin.module.ts
+++ b/client/src/app/+admin/admin.module.ts
@@ -13,6 +13,7 @@ import { JobService } from './jobs/shared/job.service'
13import { UserCreateComponent, UserListComponent, UsersComponent, UserService, UserUpdateComponent } from './users' 13import { UserCreateComponent, UserListComponent, UsersComponent, UserService, UserUpdateComponent } from './users'
14import { VideoAbuseListComponent, VideoAbusesComponent } from './video-abuses' 14import { VideoAbuseListComponent, VideoAbusesComponent } from './video-abuses'
15import { VideoBlacklistComponent, VideoBlacklistListComponent } from './video-blacklist' 15import { VideoBlacklistComponent, VideoBlacklistListComponent } from './video-blacklist'
16import { UserBanModalComponent } from '@app/+admin/users/user-list/user-ban-modal.component'
16 17
17@NgModule({ 18@NgModule({
18 imports: [ 19 imports: [
@@ -33,6 +34,7 @@ import { VideoBlacklistComponent, VideoBlacklistListComponent } from './video-bl
33 UserCreateComponent, 34 UserCreateComponent,
34 UserUpdateComponent, 35 UserUpdateComponent,
35 UserListComponent, 36 UserListComponent,
37 UserBanModalComponent,
36 38
37 VideoBlacklistComponent, 39 VideoBlacklistComponent,
38 VideoBlacklistListComponent, 40 VideoBlacklistListComponent,
diff --git a/client/src/app/+admin/users/shared/user.service.ts b/client/src/app/+admin/users/shared/user.service.ts
index 1af1e4ef2..ad7fb1eee 100644
--- a/client/src/app/+admin/users/shared/user.service.ts
+++ b/client/src/app/+admin/users/shared/user.service.ts
@@ -59,6 +59,18 @@ export class UserService {
59 .pipe(catchError(err => this.restExtractor.handleError(err))) 59 .pipe(catchError(err => this.restExtractor.handleError(err)))
60 } 60 }
61 61
62 banUser (user: User, reason?: string) {
63 const body = reason ? { reason } : {}
64
65 return this.authHttp.post(UserService.BASE_USERS_URL + user.id + '/block', body)
66 .pipe(catchError(err => this.restExtractor.handleError(err)))
67 }
68
69 unbanUser (user: User) {
70 return this.authHttp.post(UserService.BASE_USERS_URL + user.id + '/unblock', {})
71 .pipe(catchError(err => this.restExtractor.handleError(err)))
72 }
73
62 private formatUser (user: User) { 74 private formatUser (user: User) {
63 let videoQuota 75 let videoQuota
64 if (user.videoQuota === -1) { 76 if (user.videoQuota === -1) {
diff --git a/client/src/app/+admin/users/user-list/user-ban-modal.component.html b/client/src/app/+admin/users/user-list/user-ban-modal.component.html
new file mode 100644
index 000000000..b2958caa4
--- /dev/null
+++ b/client/src/app/+admin/users/user-list/user-ban-modal.component.html
@@ -0,0 +1,32 @@
1<ng-template #modal>
2 <div class="modal-header">
3 <h4 i18n class="modal-title">Ban {{ userToBan.username }}</h4>
4 <span class="close" aria-hidden="true" (click)="hideBanUserModal()"></span>
5 </div>
6
7 <div class="modal-body">
8 <form novalidate [formGroup]="form" (ngSubmit)="banUser()">
9 <div class="form-group">
10 <textarea i18n-placeholder placeholder="Reason..." formControlName="reason" [ngClass]="{ 'input-error': formErrors['reason'] }">
11 </textarea>
12 <div *ngIf="formErrors.reason" class="form-error">
13 {{ formErrors.reason }}
14 </div>
15 </div>
16
17 <div i18n>
18 A banned user will no longer be able to login.
19 </div>
20
21 <div class="form-group inputs">
22 <span i18n class="action-button action-button-cancel" (click)="hideBanUserModal()">Cancel</span>
23
24 <input
25 type="submit" i18n-value value="Ban this user" class="action-button-submit"
26 [disabled]="!form.valid"
27 >
28 </div>
29 </form>
30 </div>
31
32</ng-template> \ No newline at end of file
diff --git a/client/src/app/+admin/users/user-list/user-ban-modal.component.scss b/client/src/app/+admin/users/user-list/user-ban-modal.component.scss
new file mode 100644
index 000000000..84562f15c
--- /dev/null
+++ b/client/src/app/+admin/users/user-list/user-ban-modal.component.scss
@@ -0,0 +1,6 @@
1@import 'variables';
2@import 'mixins';
3
4textarea {
5 @include peertube-textarea(100%, 60px);
6}
diff --git a/client/src/app/+admin/users/user-list/user-ban-modal.component.ts b/client/src/app/+admin/users/user-list/user-ban-modal.component.ts
new file mode 100644
index 000000000..444de1c04
--- /dev/null
+++ b/client/src/app/+admin/users/user-list/user-ban-modal.component.ts
@@ -0,0 +1,68 @@
1import { Component, EventEmitter, OnInit, Output, ViewChild } from '@angular/core'
2import { NotificationsService } from 'angular2-notifications'
3import { FormReactive, User, UserValidatorsService } from '../../../shared'
4import { UserService } from '../shared'
5import { I18n } from '@ngx-translate/i18n-polyfill'
6import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
7import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap/modal/modal-ref'
8import { FormValidatorService } from '@app/shared/forms/form-validators/form-validator.service'
9
10@Component({
11 selector: 'my-user-ban-modal',
12 templateUrl: './user-ban-modal.component.html',
13 styleUrls: [ './user-ban-modal.component.scss' ]
14})
15export class UserBanModalComponent extends FormReactive implements OnInit {
16 @ViewChild('modal') modal: NgbModal
17 @Output() userBanned = new EventEmitter<User>()
18
19 private userToBan: User
20 private openedModal: NgbModalRef
21
22 constructor (
23 protected formValidatorService: FormValidatorService,
24 private modalService: NgbModal,
25 private notificationsService: NotificationsService,
26 private userService: UserService,
27 private userValidatorsService: UserValidatorsService,
28 private i18n: I18n
29 ) {
30 super()
31 }
32
33 ngOnInit () {
34 this.buildForm({
35 reason: this.userValidatorsService.USER_BAN_REASON
36 })
37 }
38
39 openModal (user: User) {
40 this.userToBan = user
41 this.openedModal = this.modalService.open(this.modal)
42 }
43
44 hideBanUserModal () {
45 this.userToBan = undefined
46 this.openedModal.close()
47 }
48
49 async banUser () {
50 const reason = this.form.value['reason'] || undefined
51
52 this.userService.banUser(this.userToBan, reason)
53 .subscribe(
54 () => {
55 this.notificationsService.success(
56 this.i18n('Success'),
57 this.i18n('User {{username}} banned.', { username: this.userToBan.username })
58 )
59
60 this.userBanned.emit(this.userToBan)
61 this.hideBanUserModal()
62 },
63
64 err => this.notificationsService.error(this.i18n('Error'), err.message)
65 )
66 }
67
68}
diff --git a/client/src/app/+admin/users/user-list/user-list.component.html b/client/src/app/+admin/users/user-list/user-list.component.html
index ef5a6c648..a92fe95ef 100644
--- a/client/src/app/+admin/users/user-list/user-list.component.html
+++ b/client/src/app/+admin/users/user-list/user-list.component.html
@@ -9,31 +9,50 @@
9 9
10<p-table 10<p-table
11 [value]="users" [lazy]="true" [paginator]="true" [totalRecords]="totalRecords" [rows]="rowsPerPage" 11 [value]="users" [lazy]="true" [paginator]="true" [totalRecords]="totalRecords" [rows]="rowsPerPage"
12 [sortField]="sort.field" [sortOrder]="sort.order" (onLazyLoad)="loadLazy($event)" 12 [sortField]="sort.field" [sortOrder]="sort.order" (onLazyLoad)="loadLazy($event)" dataKey="id"
13> 13>
14 <ng-template pTemplate="header"> 14 <ng-template pTemplate="header">
15 <tr> 15 <tr>
16 <th style="width: 40px"></th>
16 <th i18n pSortableColumn="username">Username <p-sortIcon field="username"></p-sortIcon></th> 17 <th i18n pSortableColumn="username">Username <p-sortIcon field="username"></p-sortIcon></th>
17 <th i18n>Email</th> 18 <th i18n>Email</th>
18 <th i18n>Video quota</th> 19 <th i18n>Video quota</th>
19 <th i18n>Role</th> 20 <th i18n>Role</th>
20 <th i18n pSortableColumn="createdAt">Created <p-sortIcon field="createdAt"></p-sortIcon></th> 21 <th i18n pSortableColumn="createdAt">Created <p-sortIcon field="createdAt"></p-sortIcon></th>
21 <th></th> 22 <th style="width: 50px;"></th>
22 </tr> 23 </tr>
23 </ng-template> 24 </ng-template>
24 25
25 <ng-template pTemplate="body" let-user> 26 <ng-template pTemplate="body" let-expanded="expanded" let-user>
26 <tr> 27
27 <td>{{ user.username }}</td> 28 <tr [ngClass]="{ banned: user.blocked }">
29 <td>
30 <span *ngIf="user.blockedReason" class="expander" [pRowToggler]="user">
31 <i [ngClass]="expanded ? 'glyphicon glyphicon-menu-down' : 'glyphicon glyphicon-menu-right'"></i>
32 </span>
33 </td>
34 <td>
35 {{ user.username }}
36 <span *ngIf="user.blocked" class="banned-info">(banned)</span>
37 </td>
28 <td>{{ user.email }}</td> 38 <td>{{ user.email }}</td>
29 <td>{{ user.videoQuota }}</td> 39 <td>{{ user.videoQuota }}</td>
30 <td>{{ user.roleLabel }}</td> 40 <td>{{ user.roleLabel }}</td>
31 <td>{{ user.createdAt }}</td> 41 <td>{{ user.createdAt }}</td>
32 <td class="action-cell"> 42 <td class="action-cell">
33 <my-action-dropdown i18n-label label="Actions" [actions]="userActions" [entry]="user"></my-action-dropdown> 43 <my-action-dropdown i18n-label label="Actions" [actions]="userActions" [entry]="user"></my-action-dropdown>
34 <!--<my-edit-button [routerLink]="getRouterUserEditLink(user)"></my-edit-button>--> 44 </td>
35 <!--<my-delete-button (click)="removeUser(user)"></my-delete-button>--> 45 </tr>
46 </ng-template>
47
48 <ng-template pTemplate="rowexpansion" let-user>
49 <tr class="user-blocked-reason">
50 <td colspan="7">
51 <span i18n class="ban-reason-label">Ban reason:</span>
52 {{ user.blockedReason }}
36 </td> 53 </td>
37 </tr> 54 </tr>
38 </ng-template> 55 </ng-template>
39</p-table> 56</p-table>
57
58<my-user-ban-modal #userBanModal (userBanned)="onUserBanned()"></my-user-ban-modal> \ No newline at end of file
diff --git a/client/src/app/+admin/users/user-list/user-list.component.scss b/client/src/app/+admin/users/user-list/user-list.component.scss
index 4fc36e11e..2d11dd7a0 100644
--- a/client/src/app/+admin/users/user-list/user-list.component.scss
+++ b/client/src/app/+admin/users/user-list/user-list.component.scss
@@ -4,3 +4,21 @@
4.add-button { 4.add-button {
5 @include create-button('../../../../assets/images/global/add.svg'); 5 @include create-button('../../../../assets/images/global/add.svg');
6} 6}
7
8my-action-dropdown /deep/ .icon {
9 &.icon-ban {
10 background-image: url('../../../../assets/images/global/edit-black.svg');
11 }
12}
13
14tr.banned {
15 color: red;
16}
17
18.banned-info {
19 font-style: italic;
20}
21
22.ban-reason-label {
23 font-weight: $font-semibold;
24} \ No newline at end of file
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 3c83859e0..f5f8f3e4a 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,4 +1,4 @@
1import { Component, OnInit } from '@angular/core' 1import { Component, OnInit, ViewChild } from '@angular/core'
2import { NotificationsService } from 'angular2-notifications' 2import { NotificationsService } from 'angular2-notifications'
3import { SortMeta } from 'primeng/components/common/sortmeta' 3import { SortMeta } from 'primeng/components/common/sortmeta'
4import { ConfirmService } from '../../../core' 4import { ConfirmService } from '../../../core'
@@ -6,6 +6,8 @@ import { RestPagination, RestTable, User } from '../../../shared'
6import { UserService } from '../shared' 6import { UserService } 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 { NgbModalRef } from '@ng-bootstrap/ng-bootstrap/modal/modal-ref'
10import { UserBanModalComponent } from '@app/+admin/users/user-list/user-ban-modal.component'
9 11
10@Component({ 12@Component({
11 selector: 'my-user-list', 13 selector: 'my-user-list',
@@ -13,6 +15,8 @@ import { DropdownAction } from '@app/shared/buttons/action-dropdown.component'
13 styleUrls: [ './user-list.component.scss' ] 15 styleUrls: [ './user-list.component.scss' ]
14}) 16})
15export class UserListComponent extends RestTable implements OnInit { 17export class UserListComponent extends RestTable implements OnInit {
18 @ViewChild('userBanModal') userBanModal: UserBanModalComponent
19
16 users: User[] = [] 20 users: User[] = []
17 totalRecords = 0 21 totalRecords = 0
18 rowsPerPage = 10 22 rowsPerPage = 10
@@ -20,6 +24,9 @@ export class UserListComponent extends RestTable implements OnInit {
20 pagination: RestPagination = { count: this.rowsPerPage, start: 0 } 24 pagination: RestPagination = { count: this.rowsPerPage, start: 0 }
21 userActions: DropdownAction<User>[] = [] 25 userActions: DropdownAction<User>[] = []
22 26
27 private userToBan: User
28 private openedModal: NgbModalRef
29
23 constructor ( 30 constructor (
24 private notificationsService: NotificationsService, 31 private notificationsService: NotificationsService,
25 private confirmService: ConfirmService, 32 private confirmService: ConfirmService,
@@ -30,12 +37,22 @@ export class UserListComponent extends RestTable implements OnInit {
30 37
31 this.userActions = [ 38 this.userActions = [
32 { 39 {
33 type: 'edit', 40 label: this.i18n('Edit'),
34 linkBuilder: this.getRouterUserEditLink 41 linkBuilder: this.getRouterUserEditLink
35 }, 42 },
36 { 43 {
37 type: 'delete', 44 label: this.i18n('Delete'),
38 handler: user => this.removeUser(user) 45 handler: user => this.removeUser(user)
46 },
47 {
48 label: this.i18n('Ban'),
49 handler: user => this.openBanUserModal(user),
50 isDisplayed: user => !user.blocked
51 },
52 {
53 label: this.i18n('Unban'),
54 handler: user => this.unbanUser(user),
55 isDisplayed: user => user.blocked
39 } 56 }
40 ] 57 ]
41 } 58 }
@@ -44,6 +61,43 @@ export class UserListComponent extends RestTable implements OnInit {
44 this.loadSort() 61 this.loadSort()
45 } 62 }
46 63
64 hideBanUserModal () {
65 this.userToBan = undefined
66 this.openedModal.close()
67 }
68
69 openBanUserModal (user: User) {
70 if (user.username === 'root') {
71 this.notificationsService.error(this.i18n('Error'), this.i18n('You cannot ban root.'))
72 return
73 }
74
75 this.userBanModal.openModal(user)
76 }
77
78 onUserBanned () {
79 this.loadData()
80 }
81
82 async unbanUser (user: User) {
83 const message = this.i18n('Do you really want to unban {{username}}?', { username: user.username })
84 const res = await this.confirmService.confirm(message, this.i18n('Unban'))
85 if (res === false) return
86
87 this.userService.unbanUser(user)
88 .subscribe(
89 () => {
90 this.notificationsService.success(
91 this.i18n('Success'),
92 this.i18n('User {{username}} unbanned.', { username: user.username })
93 )
94 this.loadData()
95 },
96
97 err => this.notificationsService.error(this.i18n('Error'), err.message)
98 )
99 }
100
47 async removeUser (user: User) { 101 async removeUser (user: User) {
48 if (user.username === 'root') { 102 if (user.username === 'root') {
49 this.notificationsService.error(this.i18n('Error'), this.i18n('You cannot delete root.')) 103 this.notificationsService.error(this.i18n('Error'), this.i18n('You cannot delete root.'))