]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blame - client/src/app/shared/shared-abuse-list/abuse-list-table.component.ts
Increase some timeouts
[github/Chocobozzz/PeerTube.git] / client / src / app / shared / shared-abuse-list / abuse-list-table.component.ts
CommitLineData
94148c90
C
1import * as debug from 'debug'
2import truncate from 'lodash-es/truncate'
3import { SortMeta } from 'primeng/api'
15a7eafb 4import { buildVideoOrPlaylistEmbed } from 'src/assets/player/utils'
94148c90 5import { environment } from 'src/environments/environment'
2e46eb97 6import { Component, Input, OnInit, ViewChild } from '@angular/core'
94148c90 7import { DomSanitizer } from '@angular/platform-browser'
5ed46c1b 8import { ActivatedRoute, Router } from '@angular/router'
94148c90
C
9import { ConfirmService, MarkdownService, Notifier, RestPagination, RestTable } from '@app/core'
10import { Account, Actor, DropdownAction, Video, VideoService } from '@app/shared/shared-main'
11import { AbuseService, BlocklistService, VideoBlockService } from '@app/shared/shared-moderation'
12import { VideoCommentService } from '@app/shared/shared-video-comment'
15a7eafb 13import { buildVideoEmbedLink, decorateVideoLink } from '@shared/core-utils'
94148c90 14import { AbuseState, AdminAbuse } from '@shared/models'
2e46eb97 15import { AdvancedInputFilter } from '../shared-forms'
94148c90
C
16import { AbuseMessageModalComponent } from './abuse-message-modal.component'
17import { ModerationCommentModalComponent } from './moderation-comment-modal.component'
18import { ProcessedAbuse } from './processed-abuse.model'
19
20const logger = debug('peertube:moderation:AbuseListTableComponent')
21
22@Component({
23 selector: 'my-abuse-list-table',
24 templateUrl: './abuse-list-table.component.html',
25 styleUrls: [ '../shared-moderation/moderation.scss', './abuse-list-table.component.scss' ]
26})
2e46eb97 27export class AbuseListTableComponent extends RestTable implements OnInit {
94148c90 28 @Input() viewType: 'admin' | 'user'
94148c90
C
29
30 @ViewChild('abuseMessagesModal', { static: true }) abuseMessagesModal: AbuseMessageModalComponent
31 @ViewChild('moderationCommentModal', { static: true }) moderationCommentModal: ModerationCommentModalComponent
32
33 abuses: ProcessedAbuse[] = []
34 totalRecords = 0
35 sort: SortMeta = { field: 'createdAt', order: 1 }
36 pagination: RestPagination = { count: this.rowsPerPage, start: 0 }
37
38 abuseActions: DropdownAction<ProcessedAbuse>[][] = []
39
1fd61899
C
40 inputFilters: AdvancedInputFilter[] = [
41 {
9df52d66 42 queryParams: { search: 'state:pending' },
1fd61899
C
43 label: $localize`Unsolved reports`
44 },
45 {
9df52d66 46 queryParams: { search: 'state:accepted' },
1fd61899
C
47 label: $localize`Accepted reports`
48 },
49 {
9df52d66 50 queryParams: { search: 'state:rejected' },
1fd61899
C
51 label: $localize`Refused reports`
52 },
53 {
9df52d66 54 queryParams: { search: 'videoIs:blacklisted' },
1fd61899
C
55 label: $localize`Reports with blocked videos`
56 },
57 {
9df52d66 58 queryParams: { search: 'videoIs:deleted' },
1fd61899
C
59 label: $localize`Reports with deleted videos`
60 }
61 ]
62
94148c90 63 constructor (
5ed46c1b
C
64 protected route: ActivatedRoute,
65 protected router: Router,
94148c90
C
66 private notifier: Notifier,
67 private abuseService: AbuseService,
68 private blocklistService: BlocklistService,
69 private commentService: VideoCommentService,
70 private videoService: VideoService,
71 private videoBlocklistService: VideoBlockService,
72 private confirmService: ConfirmService,
94148c90 73 private markdownRenderer: MarkdownService,
5ed46c1b 74 private sanitizer: DomSanitizer
94148c90
C
75 ) {
76 super()
77 }
78
79 ngOnInit () {
80 this.abuseActions = [
81 this.buildInternalActions(),
82
83 this.buildFlaggedAccountActions(),
84
85 this.buildCommentActions(),
86
87 this.buildVideoActions(),
88
89 this.buildAccountActions()
90 ]
91
92 this.initialize()
94148c90
C
93 }
94
95 isAdminView () {
96 return this.viewType === 'admin'
97 }
98
99 getIdentifier () {
100 return 'AbuseListTableComponent'
101 }
102
103 openModerationCommentModal (abuse: AdminAbuse) {
104 this.moderationCommentModal.openModal(abuse)
105 }
106
107 onModerationCommentUpdated () {
2e46eb97 108 this.reloadData()
94148c90
C
109 }
110
94148c90
C
111 isAbuseAccepted (abuse: AdminAbuse) {
112 return abuse.state.id === AbuseState.ACCEPTED
113 }
114
115 isAbuseRejected (abuse: AdminAbuse) {
116 return abuse.state.id === AbuseState.REJECTED
117 }
118
119 getVideoUrl (abuse: AdminAbuse) {
d4a8e7a6 120 return Video.buildWatchUrl(abuse.video)
94148c90
C
121 }
122
123 getCommentUrl (abuse: AdminAbuse) {
d4a8e7a6 124 return Video.buildWatchUrl(abuse.comment.video) + ';threadId=' + abuse.comment.threadId
94148c90
C
125 }
126
127 getAccountUrl (abuse: ProcessedAbuse) {
71887396 128 return '/a/' + abuse.flaggedAccount.nameWithHost
94148c90
C
129 }
130
131 getVideoEmbed (abuse: AdminAbuse) {
951b582f 132 return buildVideoOrPlaylistEmbed(
9162fdd3
C
133 decorateVideoLink({
134 url: buildVideoEmbedLink(abuse.video, environment.originServerUrl),
94148c90
C
135 title: false,
136 warningTitle: false,
7a4ea932
C
137 startTime: abuse.video.startAt,
138 stopTime: abuse.video.endAt
4097c6d6
TP
139 }),
140 abuse.video.name
94148c90
C
141 )
142 }
143
94148c90 144 async removeAbuse (abuse: AdminAbuse) {
66357162 145 const res = await this.confirmService.confirm($localize`Do you really want to delete this abuse report?`, $localize`Delete`)
94148c90
C
146 if (res === false) return
147
1378c0d3
C
148 this.abuseService.removeAbuse(abuse)
149 .subscribe({
150 next: () => {
151 this.notifier.success($localize`Abuse deleted.`)
152 this.reloadData()
153 },
94148c90 154
1378c0d3
C
155 error: err => this.notifier.error(err.message)
156 })
94148c90
C
157 }
158
159 updateAbuseState (abuse: AdminAbuse, state: AbuseState) {
160 this.abuseService.updateAbuse(abuse, { state })
1378c0d3
C
161 .subscribe({
162 next: () => this.reloadData(),
94148c90 163
1378c0d3
C
164 error: err => this.notifier.error(err.message)
165 })
94148c90
C
166 }
167
168 onCountMessagesUpdated (event: { abuseId: number, countMessages: number }) {
169 const abuse = this.abuses.find(a => a.id === event.abuseId)
170
171 if (!abuse) {
172 console.error('Cannot find abuse %d.', event.abuseId)
173 return
174 }
175
176 abuse.countMessages = event.countMessages
177 }
178
179 openAbuseMessagesModal (abuse: AdminAbuse) {
180 this.abuseMessagesModal.openModal(abuse)
181 }
182
183 isLocalAbuse (abuse: AdminAbuse) {
184 if (this.viewType === 'user') return true
185
186 return Actor.IS_LOCAL(abuse.reporterAccount.host)
187 }
188
2e46eb97 189 protected reloadData () {
94148c90
C
190 logger('Loading data.')
191
192 const options = {
193 pagination: this.pagination,
194 sort: this.sort,
195 search: this.search
196 }
197
198 const observable = this.viewType === 'admin'
199 ? this.abuseService.getAdminAbuses(options)
200 : this.abuseService.getUserAbuses(options)
201
1378c0d3
C
202 return observable.subscribe({
203 next: async resultList => {
204 this.totalRecords = resultList.total
94148c90 205
1378c0d3 206 this.abuses = []
94148c90 207
1378c0d3
C
208 for (const a of resultList.data) {
209 const abuse = a as ProcessedAbuse
94148c90 210
1378c0d3 211 abuse.reasonHtml = await this.toHtml(abuse.reason)
94148c90 212
1378c0d3
C
213 if (abuse.moderationComment) {
214 abuse.moderationCommentHtml = await this.toHtml(abuse.moderationComment)
215 }
94148c90 216
1378c0d3
C
217 if (abuse.video) {
218 abuse.embedHtml = this.sanitizer.bypassSecurityTrustHtml(this.getVideoEmbed(abuse))
94148c90 219
1378c0d3
C
220 if (abuse.video.channel?.ownerAccount) {
221 abuse.video.channel.ownerAccount = new Account(abuse.video.channel.ownerAccount)
94148c90 222 }
1378c0d3 223 }
94148c90 224
1378c0d3
C
225 if (abuse.comment) {
226 if (abuse.comment.deleted) {
227 abuse.truncatedCommentHtml = abuse.commentHtml = $localize`Deleted comment`
228 } else {
229 const truncated = truncate(abuse.comment.text, { length: 100 })
230 abuse.truncatedCommentHtml = await this.markdownRenderer.textMarkdownToHTML(truncated, true)
231 abuse.commentHtml = await this.markdownRenderer.textMarkdownToHTML(abuse.comment.text, true)
94148c90 232 }
1378c0d3 233 }
94148c90 234
1378c0d3
C
235 if (abuse.reporterAccount) {
236 abuse.reporterAccount = new Account(abuse.reporterAccount)
237 }
94148c90 238
1378c0d3
C
239 if (abuse.flaggedAccount) {
240 abuse.flaggedAccount = new Account(abuse.flaggedAccount)
241 }
94148c90 242
1378c0d3 243 if (abuse.updatedAt === abuse.createdAt) delete abuse.updatedAt
94148c90 244
1378c0d3
C
245 this.abuses.push(abuse)
246 }
247 },
94148c90 248
1378c0d3
C
249 error: err => this.notifier.error(err.message)
250 })
94148c90
C
251 }
252
253 private buildInternalActions (): DropdownAction<ProcessedAbuse>[] {
254 return [
255 {
66357162 256 label: $localize`Internal actions`,
94148c90
C
257 isHeader: true
258 },
259 {
260 label: this.isAdminView()
66357162
C
261 ? $localize`Messages with reporter`
262 : $localize`Messages with moderators`,
94148c90
C
263 handler: abuse => this.openAbuseMessagesModal(abuse),
264 isDisplayed: abuse => this.isLocalAbuse(abuse)
265 },
266 {
66357162 267 label: $localize`Update internal note`,
94148c90
C
268 handler: abuse => this.openModerationCommentModal(abuse),
269 isDisplayed: abuse => this.isAdminView() && !!abuse.moderationComment
270 },
271 {
66357162 272 label: $localize`Mark as accepted`,
94148c90
C
273 handler: abuse => this.updateAbuseState(abuse, AbuseState.ACCEPTED),
274 isDisplayed: abuse => this.isAdminView() && !this.isAbuseAccepted(abuse)
275 },
276 {
66357162 277 label: $localize`Mark as rejected`,
94148c90
C
278 handler: abuse => this.updateAbuseState(abuse, AbuseState.REJECTED),
279 isDisplayed: abuse => this.isAdminView() && !this.isAbuseRejected(abuse)
280 },
281 {
66357162 282 label: $localize`Add internal note`,
94148c90
C
283 handler: abuse => this.openModerationCommentModal(abuse),
284 isDisplayed: abuse => this.isAdminView() && !abuse.moderationComment
285 },
286 {
66357162 287 label: $localize`Delete report`,
94148c90
C
288 handler: abuse => this.isAdminView() && this.removeAbuse(abuse)
289 }
290 ]
291 }
292
293 private buildFlaggedAccountActions (): DropdownAction<ProcessedAbuse>[] {
294 if (!this.isAdminView()) return []
295
296 return [
297 {
66357162 298 label: $localize`Actions for the flagged account`,
94148c90
C
299 isHeader: true,
300 isDisplayed: abuse => abuse.flaggedAccount && !abuse.comment && !abuse.video
301 },
302
303 {
66357162 304 label: $localize`Mute account`,
94148c90
C
305 isDisplayed: abuse => abuse.flaggedAccount && !abuse.comment && !abuse.video,
306 handler: abuse => this.muteAccountHelper(abuse.flaggedAccount)
307 },
308
309 {
66357162 310 label: $localize`Mute server account`,
94148c90
C
311 isDisplayed: abuse => abuse.flaggedAccount && !abuse.comment && !abuse.video,
312 handler: abuse => this.muteServerHelper(abuse.flaggedAccount.host)
313 }
314 ]
315 }
316
317 private buildAccountActions (): DropdownAction<ProcessedAbuse>[] {
318 if (!this.isAdminView()) return []
319
320 return [
321 {
66357162 322 label: $localize`Actions for the reporter`,
94148c90
C
323 isHeader: true,
324 isDisplayed: abuse => !!abuse.reporterAccount
325 },
326
327 {
66357162 328 label: $localize`Mute reporter`,
94148c90
C
329 isDisplayed: abuse => !!abuse.reporterAccount,
330 handler: abuse => this.muteAccountHelper(abuse.reporterAccount)
331 },
332
333 {
66357162 334 label: $localize`Mute server`,
94148c90
C
335 isDisplayed: abuse => abuse.reporterAccount && !abuse.reporterAccount.userId,
336 handler: abuse => this.muteServerHelper(abuse.reporterAccount.host)
337 }
338 ]
339 }
340
341 private buildVideoActions (): DropdownAction<ProcessedAbuse>[] {
342 if (!this.isAdminView()) return []
343
344 return [
345 {
66357162 346 label: $localize`Actions for the video`,
94148c90
C
347 isHeader: true,
348 isDisplayed: abuse => abuse.video && !abuse.video.deleted
349 },
350 {
66357162 351 label: $localize`Block video`,
94148c90
C
352 isDisplayed: abuse => abuse.video && !abuse.video.deleted && !abuse.video.blacklisted,
353 handler: abuse => {
365d9083 354 this.videoBlocklistService.blockVideo(abuse.video.id, undefined, abuse.video.channel.isLocal)
1378c0d3
C
355 .subscribe({
356 next: () => {
66357162 357 this.notifier.success($localize`Video blocked.`)
94148c90
C
358
359 this.updateAbuseState(abuse, AbuseState.ACCEPTED)
360 },
361
1378c0d3
C
362 error: err => this.notifier.error(err.message)
363 })
94148c90
C
364 }
365 },
366 {
66357162 367 label: $localize`Unblock video`,
94148c90
C
368 isDisplayed: abuse => abuse.video && !abuse.video.deleted && abuse.video.blacklisted,
369 handler: abuse => {
370 this.videoBlocklistService.unblockVideo(abuse.video.id)
1378c0d3
C
371 .subscribe({
372 next: () => {
66357162 373 this.notifier.success($localize`Video unblocked.`)
94148c90
C
374
375 this.updateAbuseState(abuse, AbuseState.ACCEPTED)
376 },
377
1378c0d3
C
378 error: err => this.notifier.error(err.message)
379 })
94148c90
C
380 }
381 },
382 {
66357162 383 label: $localize`Delete video`,
94148c90
C
384 isDisplayed: abuse => abuse.video && !abuse.video.deleted,
385 handler: async abuse => {
386 const res = await this.confirmService.confirm(
66357162
C
387 $localize`Do you really want to delete this video?`,
388 $localize`Delete`
94148c90
C
389 )
390 if (res === false) return
391
392 this.videoService.removeVideo(abuse.video.id)
1378c0d3
C
393 .subscribe({
394 next: () => {
66357162 395 this.notifier.success($localize`Video deleted.`)
94148c90
C
396
397 this.updateAbuseState(abuse, AbuseState.ACCEPTED)
398 },
399
1378c0d3
C
400 error: err => this.notifier.error(err.message)
401 })
94148c90
C
402 }
403 }
404 ]
405 }
406
407 private buildCommentActions (): DropdownAction<ProcessedAbuse>[] {
408 if (!this.isAdminView()) return []
409
410 return [
411 {
66357162 412 label: $localize`Actions for the comment`,
94148c90
C
413 isHeader: true,
414 isDisplayed: abuse => abuse.comment && !abuse.comment.deleted
415 },
416
417 {
66357162 418 label: $localize`Delete comment`,
94148c90
C
419 isDisplayed: abuse => abuse.comment && !abuse.comment.deleted,
420 handler: async abuse => {
421 const res = await this.confirmService.confirm(
66357162
C
422 $localize`Do you really want to delete this comment?`,
423 $localize`Delete`
94148c90
C
424 )
425 if (res === false) return
426
427 this.commentService.deleteVideoComment(abuse.comment.video.id, abuse.comment.id)
1378c0d3
C
428 .subscribe({
429 next: () => {
66357162 430 this.notifier.success($localize`Comment deleted.`)
94148c90
C
431
432 this.updateAbuseState(abuse, AbuseState.ACCEPTED)
433 },
434
1378c0d3
C
435 error: err => this.notifier.error(err.message)
436 })
94148c90
C
437 }
438 }
439 ]
440 }
441
442 private muteAccountHelper (account: Account) {
443 this.blocklistService.blockAccountByInstance(account)
1378c0d3
C
444 .subscribe({
445 next: () => {
66357162 446 this.notifier.success($localize`Account ${account.nameWithHost} muted by the instance.`)
94148c90
C
447 account.mutedByInstance = true
448 },
449
1378c0d3
C
450 error: err => this.notifier.error(err.message)
451 })
94148c90
C
452 }
453
454 private muteServerHelper (host: string) {
455 this.blocklistService.blockServerByInstance(host)
1378c0d3
C
456 .subscribe({
457 next: () => {
66357162 458 this.notifier.success($localize`Server ${host} muted by the instance.`)
94148c90
C
459 },
460
1378c0d3
C
461 error: err => this.notifier.error(err.message)
462 })
94148c90
C
463 }
464
465 private toHtml (text: string) {
466 return this.markdownRenderer.textMarkdownToHTML(text)
467 }
468}