diff options
author | Chocobozzz <me@florianbigard.com> | 2020-07-24 17:21:25 +0200 |
---|---|---|
committer | Chocobozzz <chocobozzz@cpy.re> | 2020-07-31 11:35:19 +0200 |
commit | 441e453ae53e491b09c9b09b00b041788176ce64 (patch) | |
tree | 6104afc6b8344b39ec95211ed236ed784895d65d /client/src/app/+admin | |
parent | edbc9325462ddf4536775871ebc25e06f46612d1 (diff) | |
download | PeerTube-441e453ae53e491b09c9b09b00b041788176ce64.tar.gz PeerTube-441e453ae53e491b09c9b09b00b041788176ce64.tar.zst PeerTube-441e453ae53e491b09c9b09b00b041788176ce64.zip |
Add abuse message management in admin
Diffstat (limited to 'client/src/app/+admin')
4 files changed, 55 insertions, 18 deletions
diff --git a/client/src/app/+admin/moderation/abuse-list/abuse-list.component.html b/client/src/app/+admin/moderation/abuse-list/abuse-list.component.html index 48b31b99c..9fae5667f 100644 --- a/client/src/app/+admin/moderation/abuse-list/abuse-list.component.html +++ b/client/src/app/+admin/moderation/abuse-list/abuse-list.component.html | |||
@@ -46,6 +46,7 @@ | |||
46 | <th i18n>Video/Comment/Account</th> | 46 | <th i18n>Video/Comment/Account</th> |
47 | <th style="width: 150px;" i18n pSortableColumn="createdAt">Created <p-sortIcon field="createdAt"></p-sortIcon></th> | 47 | <th style="width: 150px;" i18n pSortableColumn="createdAt">Created <p-sortIcon field="createdAt"></p-sortIcon></th> |
48 | <th i18n pSortableColumn="state" style="width: 80px;">State <p-sortIcon field="state"></p-sortIcon></th> | 48 | <th i18n pSortableColumn="state" style="width: 80px;">State <p-sortIcon field="state"></p-sortIcon></th> |
49 | <th i18n style="width: 80px;">Messages</th> | ||
49 | <th style="width: 150px;"></th> | 50 | <th style="width: 150px;"></th> |
50 | </tr> | 51 | </tr> |
51 | </ng-template> | 52 | </ng-template> |
@@ -157,6 +158,12 @@ | |||
157 | <span *ngIf="abuse.moderationComment" container="body" placement="left auto" [ngbTooltip]="abuse.moderationComment" class="glyphicon glyphicon-comment"></span> | 158 | <span *ngIf="abuse.moderationComment" container="body" placement="left auto" [ngbTooltip]="abuse.moderationComment" class="glyphicon glyphicon-comment"></span> |
158 | </td> | 159 | </td> |
159 | 160 | ||
161 | <td class="c-hand abuse-messages" (click)="openAbuseMessagesModal(abuse)"> | ||
162 | {{ abuse.countMessages }} | ||
163 | |||
164 | <my-global-icon iconName="message-circle"></my-global-icon> | ||
165 | </td> | ||
166 | |||
160 | <td class="action-cell"> | 167 | <td class="action-cell"> |
161 | <my-action-dropdown | 168 | <my-action-dropdown |
162 | [ngClass]="{ 'show': expanded }" placement="bottom-right top-right left auto" container="body" | 169 | [ngClass]="{ 'show': expanded }" placement="bottom-right top-right left auto" container="body" |
@@ -187,3 +194,4 @@ | |||
187 | </p-table> | 194 | </p-table> |
188 | 195 | ||
189 | <my-moderation-comment-modal #moderationCommentModal (commentUpdated)="onModerationCommentUpdated()"></my-moderation-comment-modal> | 196 | <my-moderation-comment-modal #moderationCommentModal (commentUpdated)="onModerationCommentUpdated()"></my-moderation-comment-modal> |
197 | <my-abuse-message-modal #abuseMessagesModal (countMessagesUpdated)="onCountMessagesUpdated($event)"></my-abuse-message-modal> | ||
diff --git a/client/src/app/+admin/moderation/abuse-list/abuse-list.component.scss b/client/src/app/+admin/moderation/abuse-list/abuse-list.component.scss index c22f98c47..48536e3c2 100644 --- a/client/src/app/+admin/moderation/abuse-list/abuse-list.component.scss +++ b/client/src/app/+admin/moderation/abuse-list/abuse-list.component.scss | |||
@@ -21,3 +21,12 @@ | |||
21 | margin-left: 0; | 21 | margin-left: 0; |
22 | } | 22 | } |
23 | } | 23 | } |
24 | |||
25 | .abuse-messages { | ||
26 | my-global-icon { | ||
27 | width: 22px; | ||
28 | margin-left: 3px; | ||
29 | position: relative; | ||
30 | top: -2px; | ||
31 | } | ||
32 | } | ||
diff --git a/client/src/app/+admin/moderation/abuse-list/abuse-list.component.ts b/client/src/app/+admin/moderation/abuse-list/abuse-list.component.ts index 74c5fe2b3..86121fe58 100644 --- a/client/src/app/+admin/moderation/abuse-list/abuse-list.component.ts +++ b/client/src/app/+admin/moderation/abuse-list/abuse-list.component.ts | |||
@@ -8,17 +8,17 @@ import { DomSanitizer, SafeHtml } from '@angular/platform-browser' | |||
8 | import { ActivatedRoute, Params, Router } from '@angular/router' | 8 | import { ActivatedRoute, Params, Router } from '@angular/router' |
9 | import { ConfirmService, MarkdownService, Notifier, RestPagination, RestTable } from '@app/core' | 9 | import { ConfirmService, MarkdownService, Notifier, RestPagination, RestTable } from '@app/core' |
10 | import { Account, Actor, DropdownAction, Video, VideoService } from '@app/shared/shared-main' | 10 | import { Account, Actor, DropdownAction, Video, VideoService } from '@app/shared/shared-main' |
11 | import { AbuseService, BlocklistService, VideoBlockService } from '@app/shared/shared-moderation' | 11 | import { AbuseService, BlocklistService, VideoBlockService, AbuseMessageModalComponent } from '@app/shared/shared-moderation' |
12 | import { VideoCommentService } from '@app/shared/shared-video-comment' | 12 | import { VideoCommentService } from '@app/shared/shared-video-comment' |
13 | import { I18n } from '@ngx-translate/i18n-polyfill' | 13 | import { I18n } from '@ngx-translate/i18n-polyfill' |
14 | import { Abuse, AbuseState } from '@shared/models' | 14 | import { AdminAbuse, AbuseState } from '@shared/models' |
15 | import { ModerationCommentModalComponent } from './moderation-comment-modal.component' | 15 | import { ModerationCommentModalComponent } from './moderation-comment-modal.component' |
16 | 16 | ||
17 | const logger = debug('peertube:moderation:AbuseListComponent') | 17 | const logger = debug('peertube:moderation:AbuseListComponent') |
18 | 18 | ||
19 | // Don't use an abuse model because we need external services to compute some properties | 19 | // Don't use an abuse model because we need external services to compute some properties |
20 | // And this model is only used in this component | 20 | // And this model is only used in this component |
21 | export type ProcessedAbuse = Abuse & { | 21 | export type ProcessedAbuse = AdminAbuse & { |
22 | moderationCommentHtml?: string, | 22 | moderationCommentHtml?: string, |
23 | reasonHtml?: string | 23 | reasonHtml?: string |
24 | embedHtml?: SafeHtml | 24 | embedHtml?: SafeHtml |
@@ -31,8 +31,8 @@ export type ProcessedAbuse = Abuse & { | |||
31 | truncatedCommentHtml?: string | 31 | truncatedCommentHtml?: string |
32 | commentHtml?: string | 32 | commentHtml?: string |
33 | 33 | ||
34 | video: Abuse['video'] & { | 34 | video: AdminAbuse['video'] & { |
35 | channel: Abuse['video']['channel'] & { | 35 | channel: AdminAbuse['video']['channel'] & { |
36 | ownerAccount: Account | 36 | ownerAccount: Account |
37 | } | 37 | } |
38 | } | 38 | } |
@@ -45,6 +45,7 @@ export type ProcessedAbuse = Abuse & { | |||
45 | }) | 45 | }) |
46 | export class AbuseListComponent extends RestTable implements OnInit, AfterViewInit { | 46 | export class AbuseListComponent extends RestTable implements OnInit, AfterViewInit { |
47 | @ViewChild('moderationCommentModal', { static: true }) moderationCommentModal: ModerationCommentModalComponent | 47 | @ViewChild('moderationCommentModal', { static: true }) moderationCommentModal: ModerationCommentModalComponent |
48 | @ViewChild('abuseMessagesModal', { static: true }) abuseMessagesModal: AbuseMessageModalComponent | ||
48 | 49 | ||
49 | abuses: ProcessedAbuse[] = [] | 50 | abuses: ProcessedAbuse[] = [] |
50 | totalRecords = 0 | 51 | totalRecords = 0 |
@@ -104,7 +105,7 @@ export class AbuseListComponent extends RestTable implements OnInit, AfterViewIn | |||
104 | return 'AbuseListComponent' | 105 | return 'AbuseListComponent' |
105 | } | 106 | } |
106 | 107 | ||
107 | openModerationCommentModal (abuse: Abuse) { | 108 | openModerationCommentModal (abuse: AdminAbuse) { |
108 | this.moderationCommentModal.openModal(abuse) | 109 | this.moderationCommentModal.openModal(abuse) |
109 | } | 110 | } |
110 | 111 | ||
@@ -132,19 +133,19 @@ export class AbuseListComponent extends RestTable implements OnInit, AfterViewIn | |||
132 | } | 133 | } |
133 | /* END Table filter functions */ | 134 | /* END Table filter functions */ |
134 | 135 | ||
135 | isAbuseAccepted (abuse: Abuse) { | 136 | isAbuseAccepted (abuse: AdminAbuse) { |
136 | return abuse.state.id === AbuseState.ACCEPTED | 137 | return abuse.state.id === AbuseState.ACCEPTED |
137 | } | 138 | } |
138 | 139 | ||
139 | isAbuseRejected (abuse: Abuse) { | 140 | isAbuseRejected (abuse: AdminAbuse) { |
140 | return abuse.state.id === AbuseState.REJECTED | 141 | return abuse.state.id === AbuseState.REJECTED |
141 | } | 142 | } |
142 | 143 | ||
143 | getVideoUrl (abuse: Abuse) { | 144 | getVideoUrl (abuse: AdminAbuse) { |
144 | return Video.buildClientUrl(abuse.video.uuid) | 145 | return Video.buildClientUrl(abuse.video.uuid) |
145 | } | 146 | } |
146 | 147 | ||
147 | getCommentUrl (abuse: Abuse) { | 148 | getCommentUrl (abuse: AdminAbuse) { |
148 | return Video.buildClientUrl(abuse.comment.video.uuid) + ';threadId=' + abuse.comment.threadId | 149 | return Video.buildClientUrl(abuse.comment.video.uuid) + ';threadId=' + abuse.comment.threadId |
149 | } | 150 | } |
150 | 151 | ||
@@ -152,7 +153,7 @@ export class AbuseListComponent extends RestTable implements OnInit, AfterViewIn | |||
152 | return '/accounts/' + abuse.flaggedAccount.nameWithHost | 153 | return '/accounts/' + abuse.flaggedAccount.nameWithHost |
153 | } | 154 | } |
154 | 155 | ||
155 | getVideoEmbed (abuse: Abuse) { | 156 | getVideoEmbed (abuse: AdminAbuse) { |
156 | return buildVideoEmbed( | 157 | return buildVideoEmbed( |
157 | buildVideoLink({ | 158 | buildVideoLink({ |
158 | baseUrl: `${environment.embedUrl}/videos/embed/${abuse.video.uuid}`, | 159 | baseUrl: `${environment.embedUrl}/videos/embed/${abuse.video.uuid}`, |
@@ -168,7 +169,7 @@ export class AbuseListComponent extends RestTable implements OnInit, AfterViewIn | |||
168 | ($event.target as HTMLImageElement).src = Actor.GET_DEFAULT_AVATAR_URL() | 169 | ($event.target as HTMLImageElement).src = Actor.GET_DEFAULT_AVATAR_URL() |
169 | } | 170 | } |
170 | 171 | ||
171 | async removeAbuse (abuse: Abuse) { | 172 | async removeAbuse (abuse: AdminAbuse) { |
172 | const res = await this.confirmService.confirm(this.i18n('Do you really want to delete this abuse report?'), this.i18n('Delete')) | 173 | const res = await this.confirmService.confirm(this.i18n('Do you really want to delete this abuse report?'), this.i18n('Delete')) |
173 | if (res === false) return | 174 | if (res === false) return |
174 | 175 | ||
@@ -182,7 +183,7 @@ export class AbuseListComponent extends RestTable implements OnInit, AfterViewIn | |||
182 | ) | 183 | ) |
183 | } | 184 | } |
184 | 185 | ||
185 | updateAbuseState (abuse: Abuse, state: AbuseState) { | 186 | updateAbuseState (abuse: AdminAbuse, state: AbuseState) { |
186 | this.abuseService.updateAbuse(abuse, { state }) | 187 | this.abuseService.updateAbuse(abuse, { state }) |
187 | .subscribe( | 188 | .subscribe( |
188 | () => this.loadData(), | 189 | () => this.loadData(), |
@@ -191,10 +192,25 @@ export class AbuseListComponent extends RestTable implements OnInit, AfterViewIn | |||
191 | ) | 192 | ) |
192 | } | 193 | } |
193 | 194 | ||
195 | onCountMessagesUpdated (event: { abuseId: number, countMessages: number }) { | ||
196 | const abuse = this.abuses.find(a => a.id === event.abuseId) | ||
197 | |||
198 | if (!abuse) { | ||
199 | console.error('Cannot find abuse %d.', event.abuseId) | ||
200 | return | ||
201 | } | ||
202 | |||
203 | abuse.countMessages = event.countMessages | ||
204 | } | ||
205 | |||
206 | openAbuseMessagesModal (abuse: AdminAbuse) { | ||
207 | this.abuseMessagesModal.openModal(abuse) | ||
208 | } | ||
209 | |||
194 | protected loadData () { | 210 | protected loadData () { |
195 | logger('Load data.') | 211 | logger('Load data.') |
196 | 212 | ||
197 | return this.abuseService.getAbuses({ | 213 | return this.abuseService.getAdminAbuses({ |
198 | pagination: this.pagination, | 214 | pagination: this.pagination, |
199 | sort: this.sort, | 215 | sort: this.sort, |
200 | search: this.search | 216 | search: this.search |
@@ -257,7 +273,11 @@ export class AbuseListComponent extends RestTable implements OnInit, AfterViewIn | |||
257 | handler: abuse => this.removeAbuse(abuse) | 273 | handler: abuse => this.removeAbuse(abuse) |
258 | }, | 274 | }, |
259 | { | 275 | { |
260 | label: this.i18n('Add note'), | 276 | label: this.i18n('Messages'), |
277 | handler: abuse => this.openAbuseMessagesModal(abuse) | ||
278 | }, | ||
279 | { | ||
280 | label: this.i18n('Add internal note'), | ||
261 | handler: abuse => this.openModerationCommentModal(abuse), | 281 | handler: abuse => this.openModerationCommentModal(abuse), |
262 | isDisplayed: abuse => !abuse.moderationComment | 282 | isDisplayed: abuse => !abuse.moderationComment |
263 | }, | 283 | }, |
diff --git a/client/src/app/+admin/moderation/abuse-list/moderation-comment-modal.component.ts b/client/src/app/+admin/moderation/abuse-list/moderation-comment-modal.component.ts index 23738f9cd..ecb7966bf 100644 --- a/client/src/app/+admin/moderation/abuse-list/moderation-comment-modal.component.ts +++ b/client/src/app/+admin/moderation/abuse-list/moderation-comment-modal.component.ts | |||
@@ -5,7 +5,7 @@ import { AbuseService } from '@app/shared/shared-moderation' | |||
5 | import { NgbModal } from '@ng-bootstrap/ng-bootstrap' | 5 | import { NgbModal } from '@ng-bootstrap/ng-bootstrap' |
6 | import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap/modal/modal-ref' | 6 | import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap/modal/modal-ref' |
7 | import { I18n } from '@ngx-translate/i18n-polyfill' | 7 | import { I18n } from '@ngx-translate/i18n-polyfill' |
8 | import { Abuse } from '@shared/models' | 8 | import { AdminAbuse } from '@shared/models' |
9 | 9 | ||
10 | @Component({ | 10 | @Component({ |
11 | selector: 'my-moderation-comment-modal', | 11 | selector: 'my-moderation-comment-modal', |
@@ -16,7 +16,7 @@ export class ModerationCommentModalComponent extends FormReactive implements OnI | |||
16 | @ViewChild('modal', { static: true }) modal: NgbModal | 16 | @ViewChild('modal', { static: true }) modal: NgbModal |
17 | @Output() commentUpdated = new EventEmitter<string>() | 17 | @Output() commentUpdated = new EventEmitter<string>() |
18 | 18 | ||
19 | private abuseToComment: Abuse | 19 | private abuseToComment: AdminAbuse |
20 | private openedModal: NgbModalRef | 20 | private openedModal: NgbModalRef |
21 | 21 | ||
22 | constructor ( | 22 | constructor ( |
@@ -36,7 +36,7 @@ export class ModerationCommentModalComponent extends FormReactive implements OnI | |||
36 | }) | 36 | }) |
37 | } | 37 | } |
38 | 38 | ||
39 | openModal (abuseToComment: Abuse) { | 39 | openModal (abuseToComment: AdminAbuse) { |
40 | this.abuseToComment = abuseToComment | 40 | this.abuseToComment = abuseToComment |
41 | this.openedModal = this.modalService.open(this.modal, { centered: true }) | 41 | this.openedModal = this.modalService.open(this.modal, { centered: true }) |
42 | 42 | ||