]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blame - client/src/app/+admin/moderation/video-abuse-list/video-abuse-list.component.ts
Add search for video, reporter and channel name fields
[github/Chocobozzz/PeerTube.git] / client / src / app / +admin / moderation / video-abuse-list / video-abuse-list.component.ts
CommitLineData
efc9e845 1import { Component, OnInit, ViewChild } from '@angular/core'
bb152476 2import { Account } from '@app/shared/account/account.model'
f8b2c1b4 3import { Notifier } from '@app/core'
f77eb73b 4import { SortMeta } from 'primeng/api'
efc9e845 5import { VideoAbuse, VideoAbuseState } from '../../../../../../shared'
bb152476 6import { RestPagination, RestTable, VideoAbuseService, VideoBlacklistService } from '../../../shared'
b1d40cff 7import { I18n } from '@ngx-translate/i18n-polyfill'
614d1ae9
C
8import { DropdownAction } from '../../../shared/buttons/action-dropdown.component'
9import { ConfirmService } from '../../../core/index'
efc9e845 10import { ModerationCommentModalComponent } from './moderation-comment-modal.component'
614d1ae9 11import { Video } from '../../../shared/video/video.model'
1506307f 12import { MarkdownService } from '@app/shared/renderer'
d6af8146
RK
13import { Actor } from '@app/shared/actor/actor.model'
14import { buildVideoLink, buildVideoEmbed } from 'src/assets/player/utils'
15import { getAbsoluteAPIUrl } from '@app/shared/misc/utils'
16import { DomSanitizer } from '@angular/platform-browser'
bb152476 17import { BlocklistService } from '@app/shared/blocklist'
9b4241e3 18import { VideoService } from '@app/shared/video/video.service'
844db39e
RK
19import { ActivatedRoute } from '@angular/router'
20import { first } from 'rxjs/operators'
19a3b914 21
11ac88de 22@Component({
df98563e 23 selector: 'my-video-abuse-list',
f595d394 24 templateUrl: './video-abuse-list.component.html',
86521a67 25 styleUrls: [ '../moderation.component.scss', './video-abuse-list.component.scss' ]
11ac88de 26})
d592e0a9 27export class VideoAbuseListComponent extends RestTable implements OnInit {
f36da21e 28 @ViewChild('moderationCommentModal', { static: true }) moderationCommentModal: ModerationCommentModalComponent
efc9e845 29
41d71344 30 videoAbuses: (VideoAbuse & { moderationCommentHtml?: string, reasonHtml?: string })[] = []
d592e0a9 31 totalRecords = 0
86521a67 32 rowsPerPageOptions = [ 20, 50, 100 ]
9b4241e3 33 rowsPerPage = this.rowsPerPageOptions[0]
ab998f7b 34 sort: SortMeta = { field: 'createdAt', order: 1 }
d592e0a9 35 pagination: RestPagination = { count: this.rowsPerPage, start: 0 }
11ac88de 36
bb152476 37 videoAbuseActions: DropdownAction<VideoAbuse>[][] = []
efc9e845 38
df98563e 39 constructor (
f8b2c1b4 40 private notifier: Notifier,
b1d40cff 41 private videoAbuseService: VideoAbuseService,
bb152476 42 private blocklistService: BlocklistService,
9b4241e3 43 private videoService: VideoService,
bb152476 44 private videoBlacklistService: VideoBlacklistService,
efc9e845 45 private confirmService: ConfirmService,
1506307f 46 private i18n: I18n,
d6af8146 47 private markdownRenderer: MarkdownService,
844db39e
RK
48 private sanitizer: DomSanitizer,
49 private route: ActivatedRoute,
28798b5d 50 ) {
d592e0a9 51 super()
efc9e845
C
52
53 this.videoAbuseActions = [
bb152476
RK
54 [
55 {
56 label: this.i18n('Internal actions'),
57 isHeader: true
58 },
59 {
60 label: this.i18n('Delete report'),
61 handler: videoAbuse => this.removeVideoAbuse(videoAbuse)
62 },
63 {
64 label: this.i18n('Add note'),
65 handler: videoAbuse => this.openModerationCommentModal(videoAbuse),
66 isDisplayed: videoAbuse => !videoAbuse.moderationComment
67 },
68 {
69 label: this.i18n('Update note'),
70 handler: videoAbuse => this.openModerationCommentModal(videoAbuse),
71 isDisplayed: videoAbuse => !!videoAbuse.moderationComment
72 },
73 {
74 label: this.i18n('Mark as accepted'),
75 handler: videoAbuse => this.updateVideoAbuseState(videoAbuse, VideoAbuseState.ACCEPTED),
76 isDisplayed: videoAbuse => !this.isVideoAbuseAccepted(videoAbuse)
77 },
78 {
79 label: this.i18n('Mark as rejected'),
80 handler: videoAbuse => this.updateVideoAbuseState(videoAbuse, VideoAbuseState.REJECTED),
81 isDisplayed: videoAbuse => !this.isVideoAbuseRejected(videoAbuse)
82 }
83 ],
84 [
85 {
86 label: this.i18n('Actions for the video'),
9b4241e3
RK
87 isHeader: true,
88 isDisplayed: videoAbuse => !videoAbuse.video.deleted
bb152476
RK
89 },
90 {
91 label: this.i18n('Blacklist video'),
86521a67 92 isDisplayed: videoAbuse => !videoAbuse.video.deleted && !videoAbuse.video.blacklisted,
bb152476
RK
93 handler: videoAbuse => {
94 this.videoBlacklistService.blacklistVideo(videoAbuse.video.id, undefined, true)
95 .subscribe(
96 () => {
97 this.notifier.success(this.i18n('Video blacklisted.'))
98
99 this.updateVideoAbuseState(videoAbuse, VideoAbuseState.ACCEPTED)
100 },
101
9b4241e3
RK
102 err => this.notifier.error(err.message)
103 )
104 }
105 },
86521a67
RK
106 {
107 label: this.i18n('Unblacklist video'),
108 isDisplayed: videoAbuse => !videoAbuse.video.deleted && videoAbuse.video.blacklisted,
109 handler: videoAbuse => {
110 this.videoBlacklistService.removeVideoFromBlacklist(videoAbuse.video.id)
111 .subscribe(
112 () => {
113 this.notifier.success(this.i18n('Video unblacklisted.'))
114
115 this.updateVideoAbuseState(videoAbuse, VideoAbuseState.ACCEPTED)
116 },
117
118 err => this.notifier.error(err.message)
119 )
120 }
121 },
9b4241e3
RK
122 {
123 label: this.i18n('Delete video'),
124 isDisplayed: videoAbuse => !videoAbuse.video.deleted,
125 handler: async videoAbuse => {
86521a67
RK
126 const res = await this.confirmService.confirm(
127 this.i18n('Do you really want to delete this video?'),
128 this.i18n('Delete')
129 )
9b4241e3
RK
130 if (res === false) return
131
132 this.videoService.removeVideo(videoAbuse.video.id)
133 .subscribe(
134 () => {
135 this.notifier.success(this.i18n('Video deleted.'))
136
137 this.updateVideoAbuseState(videoAbuse, VideoAbuseState.ACCEPTED)
138 },
139
140 err => this.notifier.error(err.message)
141 )
142 }
143 }
144 ],
145 [
146 {
147 label: this.i18n('Actions for the reporter'),
148 isHeader: true
149 },
150 {
86521a67 151 label: this.i18n('Mute reporter'),
9b4241e3
RK
152 handler: async videoAbuse => {
153 const account = videoAbuse.reporterAccount as Account
154
155 this.blocklistService.blockAccountByInstance(account)
156 .subscribe(
157 () => {
86521a67
RK
158 this.notifier.success(
159 this.i18n('Account {{nameWithHost}} muted by the instance.', { nameWithHost: account.nameWithHost })
160 )
9b4241e3
RK
161
162 account.mutedByInstance = true
163 },
164
86521a67
RK
165 err => this.notifier.error(err.message)
166 )
167 }
168 },
169 {
170 label: this.i18n('Mute server'),
171 isDisplayed: videoAbuse => !videoAbuse.reporterAccount.userId,
172 handler: async videoAbuse => {
173 this.blocklistService.blockServerByInstance(videoAbuse.reporterAccount.host)
174 .subscribe(
175 () => {
176 this.notifier.success(
177 this.i18n('Server {{host}} muted by the instance.', { host: videoAbuse.reporterAccount.host })
178 )
179 },
180
bb152476
RK
181 err => this.notifier.error(err.message)
182 )
183 }
184 }
185 ]
efc9e845 186 ]
d592e0a9
C
187 }
188
189 ngOnInit () {
24b9417c 190 this.initialize()
844db39e
RK
191
192 this.route.queryParams
193 .pipe(first(params => params.search !== undefined && params.search !== null))
194 .subscribe(params => this.search = params.search)
df98563e 195 }
11ac88de 196
8e11a1b3
C
197 getIdentifier () {
198 return 'VideoAbuseListComponent'
199 }
200
efc9e845
C
201 openModerationCommentModal (videoAbuse: VideoAbuse) {
202 this.moderationCommentModal.openModal(videoAbuse)
203 }
204
205 onModerationCommentUpdated () {
206 this.loadData()
207 }
208
19a3b914
C
209 createByString (account: Account) {
210 return Account.CREATE_BY_STRING(account.name, account.host)
d592e0a9
C
211 }
212
efc9e845
C
213 isVideoAbuseAccepted (videoAbuse: VideoAbuse) {
214 return videoAbuse.state.id === VideoAbuseState.ACCEPTED
215 }
216
217 isVideoAbuseRejected (videoAbuse: VideoAbuse) {
218 return videoAbuse.state.id === VideoAbuseState.REJECTED
219 }
220
191764f3
C
221 getVideoUrl (videoAbuse: VideoAbuse) {
222 return Video.buildClientUrl(videoAbuse.video.uuid)
223 }
224
d6af8146
RK
225 getVideoEmbed (videoAbuse: VideoAbuse) {
226 const absoluteAPIUrl = 'http://localhost:9000' || getAbsoluteAPIUrl()
227 const embedUrl = buildVideoLink({
228 baseUrl: absoluteAPIUrl + '/videos/embed/' + videoAbuse.video.uuid,
229 warningTitle: false
230 })
231 return buildVideoEmbed(embedUrl)
232 }
233
234 switchToDefaultAvatar ($event: Event) {
235 ($event.target as HTMLImageElement).src = Actor.GET_DEFAULT_AVATAR_URL()
236 }
237
efc9e845 238 async removeVideoAbuse (videoAbuse: VideoAbuse) {
198d764f 239 const res = await this.confirmService.confirm(this.i18n('Do you really want to delete this abuse report?'), this.i18n('Delete'))
efc9e845
C
240 if (res === false) return
241
242 this.videoAbuseService.removeVideoAbuse(videoAbuse).subscribe(
243 () => {
f8b2c1b4 244 this.notifier.success(this.i18n('Abuse deleted.'))
efc9e845
C
245 this.loadData()
246 },
247
f8b2c1b4 248 err => this.notifier.error(err.message)
efc9e845
C
249 )
250 }
251
252 updateVideoAbuseState (videoAbuse: VideoAbuse, state: VideoAbuseState) {
253 this.videoAbuseService.updateVideoAbuse(videoAbuse, { state })
254 .subscribe(
255 () => this.loadData(),
256
f8b2c1b4 257 err => this.notifier.error(err.message)
efc9e845
C
258 )
259
260 }
261
d592e0a9 262 protected loadData () {
844db39e
RK
263 return this.videoAbuseService.getVideoAbuses({
264 pagination: this.pagination,
265 sort: this.sort,
266 search: this.search
267 }).subscribe(
268 async resultList => {
269 this.totalRecords = resultList.total
270
271 this.videoAbuses = resultList.data
272
273 for (const abuse of this.videoAbuses) {
274 Object.assign(abuse, {
275 reasonHtml: await this.toHtml(abuse.reason),
276 moderationCommentHtml: await this.toHtml(abuse.moderationComment),
277 embedHtml: this.sanitizer.bypassSecurityTrustHtml(this.getVideoEmbed(abuse)),
278 reporterAccount: new Account(abuse.reporterAccount)
279 })
280 }
281
282 },
283
284 err => this.notifier.error(err.message)
285 )
11ac88de 286 }
41d71344
C
287
288 private toHtml (text: string) {
289 return this.markdownRenderer.textMarkdownToHTML(text)
290 }
11ac88de 291}