aboutsummaryrefslogtreecommitdiffhomepage
path: root/client/src/app/+admin
diff options
context:
space:
mode:
authorChocobozzz <me@florianbigard.com>2018-08-13 11:54:11 +0200
committerChocobozzz <me@florianbigard.com>2018-08-14 09:27:17 +0200
commitefc9e8450a8bbeeef9cd18e3ad6037abc0f815c3 (patch)
tree4698ca4a1e0ddca385a99cd5ae3628d666f28b7d /client/src/app/+admin
parent7f7680641b6e7625d2099ff3ffc28a1a6ec5b9cf (diff)
downloadPeerTube-efc9e8450a8bbeeef9cd18e3ad6037abc0f815c3.tar.gz
PeerTube-efc9e8450a8bbeeef9cd18e3ad6037abc0f815c3.tar.zst
PeerTube-efc9e8450a8bbeeef9cd18e3ad6037abc0f815c3.zip
Add ability to delete and update abuse on client
Diffstat (limited to 'client/src/app/+admin')
-rw-r--r--client/src/app/+admin/admin.module.ts3
-rw-r--r--client/src/app/+admin/users/user-list/user-list.component.scss6
-rw-r--r--client/src/app/+admin/video-abuses/video-abuse-list/index.ts1
-rw-r--r--client/src/app/+admin/video-abuses/video-abuse-list/moderation-comment-modal.component.html32
-rw-r--r--client/src/app/+admin/video-abuses/video-abuse-list/moderation-comment-modal.component.scss6
-rw-r--r--client/src/app/+admin/video-abuses/video-abuse-list/moderation-comment-modal.component.ts73
-rw-r--r--client/src/app/+admin/video-abuses/video-abuse-list/video-abuse-list.component.html36
-rw-r--r--client/src/app/+admin/video-abuses/video-abuse-list/video-abuse-list.component.scss10
-rw-r--r--client/src/app/+admin/video-abuses/video-abuse-list/video-abuse-list.component.ts77
9 files changed, 227 insertions, 17 deletions
diff --git a/client/src/app/+admin/admin.module.ts b/client/src/app/+admin/admin.module.ts
index 04b7ec5c1..23ca5a6b3 100644
--- a/client/src/app/+admin/admin.module.ts
+++ b/client/src/app/+admin/admin.module.ts
@@ -11,7 +11,7 @@ import { JobsComponent } from './jobs/job.component'
11import { JobsListComponent } from './jobs/jobs-list/jobs-list.component' 11import { JobsListComponent } from './jobs/jobs-list/jobs-list.component'
12import { JobService } from './jobs/shared/job.service' 12import { 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 { ModerationCommentModalComponent, 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' 16import { UserBanModalComponent } from '@app/+admin/users/user-list/user-ban-modal.component'
17 17
@@ -41,6 +41,7 @@ import { UserBanModalComponent } from '@app/+admin/users/user-list/user-ban-moda
41 41
42 VideoAbusesComponent, 42 VideoAbusesComponent,
43 VideoAbuseListComponent, 43 VideoAbuseListComponent,
44 ModerationCommentModalComponent,
44 45
45 JobsComponent, 46 JobsComponent,
46 JobsListComponent, 47 JobsListComponent,
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 2d11dd7a0..47291918d 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
@@ -5,12 +5,6 @@
5 @include create-button('../../../../assets/images/global/add.svg'); 5 @include create-button('../../../../assets/images/global/add.svg');
6} 6}
7 7
8my-action-dropdown /deep/ .icon {
9 &.icon-ban {
10 background-image: url('../../../../assets/images/global/edit-black.svg');
11 }
12}
13
14tr.banned { 8tr.banned {
15 color: red; 9 color: red;
16} 10}
diff --git a/client/src/app/+admin/video-abuses/video-abuse-list/index.ts b/client/src/app/+admin/video-abuses/video-abuse-list/index.ts
index 01c24d860..da7176e52 100644
--- a/client/src/app/+admin/video-abuses/video-abuse-list/index.ts
+++ b/client/src/app/+admin/video-abuses/video-abuse-list/index.ts
@@ -1 +1,2 @@
1export * from './video-abuse-list.component' 1export * from './video-abuse-list.component'
2export * from './moderation-comment-modal.component'
diff --git a/client/src/app/+admin/video-abuses/video-abuse-list/moderation-comment-modal.component.html b/client/src/app/+admin/video-abuses/video-abuse-list/moderation-comment-modal.component.html
new file mode 100644
index 000000000..3a8424f68
--- /dev/null
+++ b/client/src/app/+admin/video-abuses/video-abuse-list/moderation-comment-modal.component.html
@@ -0,0 +1,32 @@
1<ng-template #modal>
2 <div class="modal-header">
3 <h4 i18n class="modal-title">Moderation comment</h4>
4 <span class="close" aria-hidden="true" (click)="hideModerationCommentModal()"></span>
5 </div>
6
7 <div class="modal-body">
8 <form novalidate [formGroup]="form" (ngSubmit)="banUser()">
9 <div class="form-group">
10 <textarea formControlName="moderationComment" [ngClass]="{ 'input-error': formErrors['moderationComment'] }">
11 </textarea>
12 <div *ngIf="formErrors.moderationComment" class="form-error">
13 {{ formErrors.moderationComment }}
14 </div>
15 </div>
16
17 <div i18n>
18 This comment can only be seen by you or the other moderators.
19 </div>
20
21 <div class="form-group inputs">
22 <span i18n class="action-button action-button-cancel" (click)="hideModerationCommentModal()">Cancel</span>
23
24 <input
25 type="submit" i18n-value value="Update this comment" 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/video-abuses/video-abuse-list/moderation-comment-modal.component.scss b/client/src/app/+admin/video-abuses/video-abuse-list/moderation-comment-modal.component.scss
new file mode 100644
index 000000000..afcdb9a16
--- /dev/null
+++ b/client/src/app/+admin/video-abuses/video-abuse-list/moderation-comment-modal.component.scss
@@ -0,0 +1,6 @@
1@import 'variables';
2@import 'mixins';
3
4textarea {
5 @include peertube-textarea(100%, 100px);
6}
diff --git a/client/src/app/+admin/video-abuses/video-abuse-list/moderation-comment-modal.component.ts b/client/src/app/+admin/video-abuses/video-abuse-list/moderation-comment-modal.component.ts
new file mode 100644
index 000000000..7e8af6e5a
--- /dev/null
+++ b/client/src/app/+admin/video-abuses/video-abuse-list/moderation-comment-modal.component.ts
@@ -0,0 +1,73 @@
1import { Component, EventEmitter, OnInit, Output, ViewChild } from '@angular/core'
2import { NotificationsService } from 'angular2-notifications'
3import { FormReactive, VideoAbuseService, VideoAbuseValidatorsService } from '../../../shared'
4import { I18n } from '@ngx-translate/i18n-polyfill'
5import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
6import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap/modal/modal-ref'
7import { FormValidatorService } from '@app/shared/forms/form-validators/form-validator.service'
8import { VideoAbuse } from '../../../../../../shared/models/videos'
9
10@Component({
11 selector: 'my-moderation-comment-modal',
12 templateUrl: './moderation-comment-modal.component.html',
13 styleUrls: [ './moderation-comment-modal.component.scss' ]
14})
15export class ModerationCommentModalComponent extends FormReactive implements OnInit {
16 @ViewChild('modal') modal: NgbModal
17 @Output() commentUpdated = new EventEmitter<string>()
18
19 private abuseToComment: VideoAbuse
20 private openedModal: NgbModalRef
21
22 constructor (
23 protected formValidatorService: FormValidatorService,
24 private modalService: NgbModal,
25 private notificationsService: NotificationsService,
26 private videoAbuseService: VideoAbuseService,
27 private videoAbuseValidatorsService: VideoAbuseValidatorsService,
28 private i18n: I18n
29 ) {
30 super()
31 }
32
33 ngOnInit () {
34 this.buildForm({
35 moderationComment: this.videoAbuseValidatorsService.VIDEO_ABUSE_REASON
36 })
37 }
38
39 openModal (abuseToComment: VideoAbuse) {
40 this.abuseToComment = abuseToComment
41 this.openedModal = this.modalService.open(this.modal)
42
43 this.form.patchValue({
44 moderationComment: this.abuseToComment.moderationComment
45 })
46 }
47
48 hideModerationCommentModal () {
49 this.abuseToComment = undefined
50 this.openedModal.close()
51 this.form.reset()
52 }
53
54 async banUser () {
55 const moderationComment: string = this.form.value['moderationComment']
56
57 this.videoAbuseService.updateVideoAbuse(this.abuseToComment, { moderationComment })
58 .subscribe(
59 () => {
60 this.notificationsService.success(
61 this.i18n('Success'),
62 this.i18n('Comment updated.')
63 )
64
65 this.commentUpdated.emit(moderationComment)
66 this.hideModerationCommentModal()
67 },
68
69 err => this.notificationsService.error(this.i18n('Error'), err.message)
70 )
71 }
72
73}
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 8111e5f73..08501d872 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
@@ -4,31 +4,63 @@
4 4
5<p-table 5<p-table
6 [value]="videoAbuses" [lazy]="true" [paginator]="true" [totalRecords]="totalRecords" [rows]="rowsPerPage" 6 [value]="videoAbuses" [lazy]="true" [paginator]="true" [totalRecords]="totalRecords" [rows]="rowsPerPage"
7 [sortField]="sort.field" [sortOrder]="sort.order" (onLazyLoad)="loadLazy($event)" 7 [sortField]="sort.field" [sortOrder]="sort.order" (onLazyLoad)="loadLazy($event)" dataKey="id"
8> 8>
9 <ng-template pTemplate="header"> 9 <ng-template pTemplate="header">
10 <tr> 10 <tr>
11 <th style="width: 40px"></th>
12 <th i18n style="width: 80px;">State</th>
11 <th i18n>Reason</th> 13 <th i18n>Reason</th>
12 <th i18n>Reporter</th> 14 <th i18n>Reporter</th>
13 <th i18n pSortableColumn="createdAt">Created <p-sortIcon field="createdAt"></p-sortIcon></th> 15 <th i18n pSortableColumn="createdAt">Created <p-sortIcon field="createdAt"></p-sortIcon></th>
14 <th i18n>Video</th> 16 <th i18n>Video</th>
17 <th style="width: 50px;"></th>
15 </tr> 18 </tr>
16 </ng-template> 19 </ng-template>
17 20
18 <ng-template pTemplate="body" let-videoAbuse> 21 <ng-template pTemplate="body" let-expanded="expanded" let-videoAbuse>
19 <tr> 22 <tr>
23 <td>
24 <span *ngIf="videoAbuse.moderationComment" class="expander" [pRowToggler]="videoAbuse">
25 <i [ngClass]="expanded ? 'glyphicon glyphicon-menu-down' : 'glyphicon glyphicon-menu-right'"></i>
26 </span>
27 </td>
28
29 <td>
30 <span *ngIf="isVideoAbuseAccepted(videoAbuse)" [title]="videoAbuse.state.label" class="glyphicon glyphicon-ok"></span>
31 <span *ngIf="isVideoAbuseRejected(videoAbuse)" [title]="videoAbuse.state.label" class="glyphicon glyphicon-remove"></span>
32 </td>
33
20 <td>{{ videoAbuse.reason }}</td> 34 <td>{{ videoAbuse.reason }}</td>
35
21 <td> 36 <td>
22 <a [href]="videoAbuse.reporterAccount.url" i18n-title title="Go to the account" target="_blank" rel="noopener noreferrer"> 37 <a [href]="videoAbuse.reporterAccount.url" i18n-title title="Go to the account" target="_blank" rel="noopener noreferrer">
23 {{ createByString(videoAbuse.reporterAccount) }} 38 {{ createByString(videoAbuse.reporterAccount) }}
24 </a> 39 </a>
25 </td> 40 </td>
41
26 <td>{{ videoAbuse.createdAt }}</td> 42 <td>{{ videoAbuse.createdAt }}</td>
43
27 <td> 44 <td>
28 <a [href]="videoAbuse.video.url" i18n-title title="Go to the video" target="_blank" rel="noopener noreferrer"> 45 <a [href]="videoAbuse.video.url" i18n-title title="Go to the video" target="_blank" rel="noopener noreferrer">
29 {{ videoAbuse.video.name }} 46 {{ videoAbuse.video.name }}
30 </a> 47 </a>
31 </td> 48 </td>
49
50 <td class="action-cell">
51 <my-action-dropdown i18n-label label="Actions" [actions]="videoAbuseActions" [entry]="videoAbuse"></my-action-dropdown>
52 </td>
53 </tr>
54 </ng-template>
55
56 <ng-template pTemplate="rowexpansion" let-videoAbuse>
57 <tr class="moderation-comment">
58 <td colspan="7">
59 <span i18n class="moderation-comment-label">Moderation comment:</span>
60 {{ videoAbuse.moderationComment }}
61 </td>
32 </tr> 62 </tr>
33 </ng-template> 63 </ng-template>
34</p-table> 64</p-table>
65
66<my-moderation-comment-modal #moderationCommentModal (commentUpdated)="onModerationCommentUpdated()"></my-moderation-comment-modal> \ No newline at end of file
diff --git a/client/src/app/+admin/video-abuses/video-abuse-list/video-abuse-list.component.scss b/client/src/app/+admin/video-abuses/video-abuse-list/video-abuse-list.component.scss
index 6a4762650..951a3fc92 100644
--- a/client/src/app/+admin/video-abuses/video-abuse-list/video-abuse-list.component.scss
+++ b/client/src/app/+admin/video-abuses/video-abuse-list/video-abuse-list.component.scss
@@ -1,6 +1,6 @@
1/deep/ a { 1@import '_variables';
2@import '_mixins';
2 3
3 &, &:hover, &:active, &:focus { 4.moderation-comment-label {
4 color: #000; 5 font-weight: $font-semibold;
5 } 6} \ No newline at end of file
6}
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 6ddebff7e..a850c0ec2 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
@@ -1,11 +1,13 @@
1import { Component, OnInit } from '@angular/core' 1import { Component, OnInit, ViewChild } from '@angular/core'
2import { Account } from '@app/shared/account/account.model' 2import { Account } from '@app/shared/account/account.model'
3import { NotificationsService } from 'angular2-notifications' 3import { NotificationsService } from 'angular2-notifications'
4import { SortMeta } from 'primeng/components/common/sortmeta' 4import { SortMeta } from 'primeng/components/common/sortmeta'
5import { VideoAbuse } from '../../../../../../shared' 5import { VideoAbuse, VideoAbuseState } from '../../../../../../shared'
6
7import { RestPagination, RestTable, VideoAbuseService } from '../../../shared' 6import { RestPagination, RestTable, VideoAbuseService } from '../../../shared'
8import { I18n } from '@ngx-translate/i18n-polyfill' 7import { I18n } from '@ngx-translate/i18n-polyfill'
8import { DropdownAction } from '@app/shared/buttons/action-dropdown.component'
9import { ConfirmService } from '@app/core'
10import { ModerationCommentModalComponent } from './moderation-comment-modal.component'
9 11
10@Component({ 12@Component({
11 selector: 'my-video-abuse-list', 13 selector: 'my-video-abuse-list',
@@ -13,28 +15,97 @@ import { I18n } from '@ngx-translate/i18n-polyfill'
13 styleUrls: [ './video-abuse-list.component.scss'] 15 styleUrls: [ './video-abuse-list.component.scss']
14}) 16})
15export class VideoAbuseListComponent extends RestTable implements OnInit { 17export class VideoAbuseListComponent extends RestTable implements OnInit {
18 @ViewChild('moderationCommentModal') moderationCommentModal: ModerationCommentModalComponent
19
16 videoAbuses: VideoAbuse[] = [] 20 videoAbuses: VideoAbuse[] = []
17 totalRecords = 0 21 totalRecords = 0
18 rowsPerPage = 10 22 rowsPerPage = 10
19 sort: SortMeta = { field: 'createdAt', order: 1 } 23 sort: SortMeta = { field: 'createdAt', order: 1 }
20 pagination: RestPagination = { count: this.rowsPerPage, start: 0 } 24 pagination: RestPagination = { count: this.rowsPerPage, start: 0 }
21 25
26 videoAbuseActions: DropdownAction<VideoAbuse>[] = []
27
22 constructor ( 28 constructor (
23 private notificationsService: NotificationsService, 29 private notificationsService: NotificationsService,
24 private videoAbuseService: VideoAbuseService, 30 private videoAbuseService: VideoAbuseService,
31 private confirmService: ConfirmService,
25 private i18n: I18n 32 private i18n: I18n
26 ) { 33 ) {
27 super() 34 super()
35
36 this.videoAbuseActions = [
37 {
38 label: this.i18n('Delete'),
39 handler: videoAbuse => this.removeVideoAbuse(videoAbuse)
40 },
41 {
42 label: this.i18n('Update moderation comment'),
43 handler: videoAbuse => this.openModerationCommentModal(videoAbuse)
44 },
45 {
46 label: this.i18n('Mark as accepted'),
47 handler: videoAbuse => this.updateVideoAbuseState(videoAbuse, VideoAbuseState.ACCEPTED),
48 isDisplayed: videoAbuse => !this.isVideoAbuseAccepted(videoAbuse)
49 },
50 {
51 label: this.i18n('Mark as rejected'),
52 handler: videoAbuse => this.updateVideoAbuseState(videoAbuse, VideoAbuseState.REJECTED),
53 isDisplayed: videoAbuse => !this.isVideoAbuseRejected(videoAbuse)
54 }
55 ]
28 } 56 }
29 57
30 ngOnInit () { 58 ngOnInit () {
31 this.loadSort() 59 this.loadSort()
32 } 60 }
33 61
62 openModerationCommentModal (videoAbuse: VideoAbuse) {
63 this.moderationCommentModal.openModal(videoAbuse)
64 }
65
66 onModerationCommentUpdated () {
67 this.loadData()
68 }
69
34 createByString (account: Account) { 70 createByString (account: Account) {
35 return Account.CREATE_BY_STRING(account.name, account.host) 71 return Account.CREATE_BY_STRING(account.name, account.host)
36 } 72 }
37 73
74 isVideoAbuseAccepted (videoAbuse: VideoAbuse) {
75 return videoAbuse.state.id === VideoAbuseState.ACCEPTED
76 }
77
78 isVideoAbuseRejected (videoAbuse: VideoAbuse) {
79 return videoAbuse.state.id === VideoAbuseState.REJECTED
80 }
81
82 async removeVideoAbuse (videoAbuse: VideoAbuse) {
83 const res = await this.confirmService.confirm(this.i18n('Do you really want to delete this abuse?'), this.i18n('Delete'))
84 if (res === false) return
85
86 this.videoAbuseService.removeVideoAbuse(videoAbuse).subscribe(
87 () => {
88 this.notificationsService.success(
89 this.i18n('Success'),
90 this.i18n('Abuse deleted.')
91 )
92 this.loadData()
93 },
94
95 err => this.notificationsService.error(this.i18n('Error'), err.message)
96 )
97 }
98
99 updateVideoAbuseState (videoAbuse: VideoAbuse, state: VideoAbuseState) {
100 this.videoAbuseService.updateVideoAbuse(videoAbuse, { state })
101 .subscribe(
102 () => this.loadData(),
103
104 err => this.notificationsService.error(this.i18n('Error'), err.message)
105 )
106
107 }
108
38 protected loadData () { 109 protected loadData () {
39 return this.videoAbuseService.getVideoAbuses(this.pagination, this.sort) 110 return this.videoAbuseService.getVideoAbuses(this.pagination, this.sort)
40 .subscribe( 111 .subscribe(