aboutsummaryrefslogtreecommitdiffhomepage
path: root/client/src/app/+admin
diff options
context:
space:
mode:
authorChocobozzz <me@florianbigard.com>2020-07-01 16:05:30 +0200
committerChocobozzz <chocobozzz@cpy.re>2020-07-10 14:02:41 +0200
commitd95d15598847c7f020aa056e7e6e0c02d2bbf732 (patch)
treea8a593f1269688caf9e5f99559996f346290fec5 /client/src/app/+admin
parent72493e44e9b455a04c4f093ed6c6ffa300b98d8b (diff)
downloadPeerTube-d95d15598847c7f020aa056e7e6e0c02d2bbf732.tar.gz
PeerTube-d95d15598847c7f020aa056e7e6e0c02d2bbf732.tar.zst
PeerTube-d95d15598847c7f020aa056e7e6e0c02d2bbf732.zip
Use 3 tables to represent abuses
Diffstat (limited to 'client/src/app/+admin')
-rw-r--r--client/src/app/+admin/admin.component.ts2
-rw-r--r--client/src/app/+admin/admin.module.ts10
-rw-r--r--client/src/app/+admin/moderation/abuse-list/abuse-details.component.html93
-rw-r--r--client/src/app/+admin/moderation/abuse-list/abuse-details.component.ts (renamed from client/src/app/+admin/moderation/video-abuse-list/video-abuse-details.component.ts)22
-rw-r--r--client/src/app/+admin/moderation/abuse-list/abuse-list.component.html (renamed from client/src/app/+admin/moderation/video-abuse-list/video-abuse-list.component.html)64
-rw-r--r--client/src/app/+admin/moderation/abuse-list/abuse-list.component.scss (renamed from client/src/app/+admin/moderation/video-abuse-list/video-abuse-list.component.scss)0
-rw-r--r--client/src/app/+admin/moderation/abuse-list/abuse-list.component.ts (renamed from client/src/app/+admin/moderation/video-abuse-list/video-abuse-list.component.ts)127
-rw-r--r--client/src/app/+admin/moderation/abuse-list/index.ts3
-rw-r--r--client/src/app/+admin/moderation/abuse-list/moderation-comment-modal.component.html (renamed from client/src/app/+admin/moderation/video-abuse-list/moderation-comment-modal.component.html)0
-rw-r--r--client/src/app/+admin/moderation/abuse-list/moderation-comment-modal.component.scss (renamed from client/src/app/+admin/moderation/video-abuse-list/moderation-comment-modal.component.scss)0
-rw-r--r--client/src/app/+admin/moderation/abuse-list/moderation-comment-modal.component.ts (renamed from client/src/app/+admin/moderation/video-abuse-list/moderation-comment-modal.component.ts)18
-rw-r--r--client/src/app/+admin/moderation/index.ts2
-rw-r--r--client/src/app/+admin/moderation/moderation.routes.ts15
-rw-r--r--client/src/app/+admin/moderation/video-abuse-list/index.ts2
-rw-r--r--client/src/app/+admin/moderation/video-abuse-list/video-abuse-details.component.html93
15 files changed, 230 insertions, 221 deletions
diff --git a/client/src/app/+admin/admin.component.ts b/client/src/app/+admin/admin.component.ts
index 6f340884f..1e137e63e 100644
--- a/client/src/app/+admin/admin.component.ts
+++ b/client/src/app/+admin/admin.component.ts
@@ -91,7 +91,7 @@ export class AdminComponent implements OnInit {
91 } 91 }
92 92
93 hasVideoAbusesRight () { 93 hasVideoAbusesRight () {
94 return this.auth.getUser().hasRight(UserRight.MANAGE_VIDEO_ABUSES) 94 return this.auth.getUser().hasRight(UserRight.MANAGE_ABUSES)
95 } 95 }
96 96
97 hasVideoBlocklistRight () { 97 hasVideoBlocklistRight () {
diff --git a/client/src/app/+admin/admin.module.ts b/client/src/app/+admin/admin.module.ts
index 728227a84..c59bd2927 100644
--- a/client/src/app/+admin/admin.module.ts
+++ b/client/src/app/+admin/admin.module.ts
@@ -14,10 +14,10 @@ import { FollowersListComponent, FollowsComponent, VideoRedundanciesListComponen
14import { FollowingListComponent } from './follows/following-list/following-list.component' 14import { FollowingListComponent } from './follows/following-list/following-list.component'
15import { RedundancyCheckboxComponent } from './follows/shared/redundancy-checkbox.component' 15import { RedundancyCheckboxComponent } from './follows/shared/redundancy-checkbox.component'
16import { VideoRedundancyInformationComponent } from './follows/video-redundancies-list/video-redundancy-information.component' 16import { VideoRedundancyInformationComponent } from './follows/video-redundancies-list/video-redundancy-information.component'
17import { ModerationCommentModalComponent, VideoAbuseListComponent, VideoBlockListComponent } from './moderation' 17import { ModerationCommentModalComponent, AbuseListComponent, VideoBlockListComponent } from './moderation'
18import { InstanceAccountBlocklistComponent, InstanceServerBlocklistComponent } from './moderation/instance-blocklist' 18import { InstanceAccountBlocklistComponent, InstanceServerBlocklistComponent } from './moderation/instance-blocklist'
19import { ModerationComponent } from './moderation/moderation.component' 19import { ModerationComponent } from './moderation/moderation.component'
20import { VideoAbuseDetailsComponent } from './moderation/video-abuse-list/video-abuse-details.component' 20import { AbuseDetailsComponent } from './moderation/abuse-list/abuse-details.component'
21import { PluginListInstalledComponent } from './plugins/plugin-list-installed/plugin-list-installed.component' 21import { PluginListInstalledComponent } from './plugins/plugin-list-installed/plugin-list-installed.component'
22import { PluginSearchComponent } from './plugins/plugin-search/plugin-search.component' 22import { PluginSearchComponent } from './plugins/plugin-search/plugin-search.component'
23import { PluginShowInstalledComponent } from './plugins/plugin-show-installed/plugin-show-installed.component' 23import { PluginShowInstalledComponent } from './plugins/plugin-show-installed/plugin-show-installed.component'
@@ -60,8 +60,10 @@ import { UserCreateComponent, UserListComponent, UserPasswordComponent, UsersCom
60 60
61 ModerationComponent, 61 ModerationComponent,
62 VideoBlockListComponent, 62 VideoBlockListComponent,
63 VideoAbuseListComponent, 63
64 VideoAbuseDetailsComponent, 64 AbuseListComponent,
65 AbuseDetailsComponent,
66
65 ModerationCommentModalComponent, 67 ModerationCommentModalComponent,
66 InstanceServerBlocklistComponent, 68 InstanceServerBlocklistComponent,
67 InstanceAccountBlocklistComponent, 69 InstanceAccountBlocklistComponent,
diff --git a/client/src/app/+admin/moderation/abuse-list/abuse-details.component.html b/client/src/app/+admin/moderation/abuse-list/abuse-details.component.html
new file mode 100644
index 000000000..d031ea8ed
--- /dev/null
+++ b/client/src/app/+admin/moderation/abuse-list/abuse-details.component.html
@@ -0,0 +1,93 @@
1<div class="d-flex moderation-expanded">
2 <!-- report left part (report details) -->
3 <div class="col-8">
4
5 <!-- report metadata -->
6 <div class="d-flex">
7 <span class="col-3 moderation-expanded-label" i18n>Reporter</span>
8 <span class="col-9 moderation-expanded-text">
9 <a [routerLink]="[ '/admin/moderation/abuses/list' ]" [queryParams]="{ 'search': 'reporter:&quot;' + abuse.reporterAccount.displayName + '&quot;' }" class="chip">
10 <img
11 class="avatar"
12 [src]="abuse.reporterAccount.avatar?.path"
13 (error)="switchToDefaultAvatar($event)"
14 alt="Avatar"
15 >
16 <div>
17 <span class="text-muted">{{ abuse.reporterAccount.nameWithHost }}</span>
18 </div>
19 </a>
20 <a [routerLink]="[ '/admin/moderation/abuses/list' ]" [queryParams]="{ 'search': 'reporter:&quot;' + abuse.reporterAccount.displayName + '&quot;' }" class="ml-auto text-muted video-details-links" i18n>
21 {abuse.countReportsForReporter, plural, =1 {1 report} other {{{ abuse.countReportsForReporter }} reports}}<span class="ml-1 glyphicon glyphicon-flag"></span>
22 </a>
23 </span>
24 </div>
25
26 <div class="d-flex">
27 <span class="col-3 moderation-expanded-label" i18n>Reportee</span>
28 <span class="col-9 moderation-expanded-text">
29 <a [routerLink]="[ '/admin/moderation/abuses/list' ]" [queryParams]="{ 'search': 'reportee:&quot;' +abuse.video.channel.ownerAccount.displayName + '&quot;' }" class="chip">
30 <img
31 class="avatar"
32 [src]="abuse.video.channel.ownerAccount?.avatar?.path"
33 (error)="switchToDefaultAvatar($event)"
34 alt="Avatar"
35 >
36 <div>
37 <span class="text-muted">{{ abuse.video.channel.ownerAccount ? abuse.video.channel.ownerAccount.nameWithHost : '' }}</span>
38 </div>
39 </a>
40 <a [routerLink]="[ '/admin/moderation/abuses/list' ]" [queryParams]="{ 'search': 'reportee:&quot;' +abuse.video.channel.ownerAccount.displayName + '&quot;' }" class="ml-auto text-muted video-details-links" i18n>
41 {abuse.countReportsForReportee, plural, =1 {1 report} other {{{ abuse.countReportsForReportee }} reports}}<span class="ml-1 glyphicon glyphicon-flag"></span>
42 </a>
43 </span>
44 </div>
45
46 <div class="d-flex" *ngIf="abuse.updatedAt">
47 <span class="col-3 moderation-expanded-label" i18n>Updated</span>
48 <time class="col-9 moderation-expanded-text video-details-date-updated">{{ abuse.updatedAt | date: 'medium' }}</time>
49 </div>
50
51 <!-- report text -->
52 <div class="mt-3 d-flex">
53 <span class="col-3 moderation-expanded-label">
54 <ng-container i18n>Report</ng-container>
55 <a [routerLink]="[ '/admin/moderation/abuses/list' ]" [queryParams]="{ 'search': '#' + abuse.id }" class="ml-1 text-muted">#{{ abuse.id }}</a>
56 </span>
57 <span class="col-9 moderation-expanded-text" [innerHTML]="abuse.reasonHtml"></span>
58 </div>
59
60 <div *ngIf="getPredefinedReasons()" class="mt-2 d-flex">
61 <span class="col-3"></span>
62 <span class="col-9">
63 <a [routerLink]="[ '/admin/moderation/abuses/list' ]" [queryParams]="{ 'search': 'tag:' + reason.id }" class="chip rectangular bg-secondary text-light" *ngFor="let reason of getPredefinedReasons()">
64 <div>{{ reason.label }}</div>
65 </a>
66 </span>
67 </div>
68
69 <div *ngIf="abuse.startAt" class="mt-2 d-flex">
70 <span class="col-3 moderation-expanded-label" i18n>Reported part</span>
71 <span class="col-9">
72 {{ startAt }}<ng-container *ngIf="abuse.endAt"> - {{ endAt }}</ng-container>
73 </span>
74 </div>
75
76 <div class="mt-3 d-flex" *ngIf="abuse.moderationComment">
77 <span class="col-3 moderation-expanded-label" i18n>Note</span>
78 <span class="col-9 moderation-expanded-text" [innerHTML]="abuse.moderationCommentHtml"></span>
79 </div>
80
81 </div>
82
83 <!-- report right part (video details) -->
84 <div class="col-4">
85 <div class="screenratio">
86 <div *ngIf="abuse.video.deleted || abuse.video.blacklisted">
87 <span i18n *ngIf="abuse.video.deleted">The video was deleted</span>
88 <span i18n *ngIf="!abuse.video.deleted">The video was blocked</span>
89 </div>
90 <div *ngIf="!abuse.video.deleted && !abuse.video.blacklisted" [innerHTML]="abuse.embedHtml"></div>
91 </div>
92 </div>
93</div>
diff --git a/client/src/app/+admin/moderation/video-abuse-list/video-abuse-details.component.ts b/client/src/app/+admin/moderation/abuse-list/abuse-details.component.ts
index 5db2887fa..8f87630b8 100644
--- a/client/src/app/+admin/moderation/video-abuse-list/video-abuse-details.component.ts
+++ b/client/src/app/+admin/moderation/abuse-list/abuse-details.component.ts
@@ -1,19 +1,19 @@
1import { Component, Input } from '@angular/core' 1import { Component, Input } from '@angular/core'
2import { Actor } from '@app/shared/shared-main' 2import { Actor } from '@app/shared/shared-main'
3import { I18n } from '@ngx-translate/i18n-polyfill' 3import { I18n } from '@ngx-translate/i18n-polyfill'
4import { VideoAbusePredefinedReasonsString } from '../../../../../../shared/models/videos/abuse/video-abuse-reason.model' 4import { AbusePredefinedReasonsString } from '@shared/models'
5import { ProcessedVideoAbuse } from './video-abuse-list.component' 5import { ProcessedAbuse } from './abuse-list.component'
6import { durationToString } from '@app/helpers' 6import { durationToString } from '@app/helpers'
7 7
8@Component({ 8@Component({
9 selector: 'my-video-abuse-details', 9 selector: 'my-abuse-details',
10 templateUrl: './video-abuse-details.component.html', 10 templateUrl: './abuse-details.component.html',
11 styleUrls: [ '../moderation.component.scss' ] 11 styleUrls: [ '../moderation.component.scss' ]
12}) 12})
13export class VideoAbuseDetailsComponent { 13export class AbuseDetailsComponent {
14 @Input() videoAbuse: ProcessedVideoAbuse 14 @Input() abuse: ProcessedAbuse
15 15
16 private predefinedReasonsTranslations: { [key in VideoAbusePredefinedReasonsString]: string } 16 private predefinedReasonsTranslations: { [key in AbusePredefinedReasonsString]: string }
17 17
18 constructor ( 18 constructor (
19 private i18n: I18n 19 private i18n: I18n
@@ -31,16 +31,16 @@ export class VideoAbuseDetailsComponent {
31 } 31 }
32 32
33 get startAt () { 33 get startAt () {
34 return durationToString(this.videoAbuse.startAt) 34 return durationToString(this.abuse.startAt)
35 } 35 }
36 36
37 get endAt () { 37 get endAt () {
38 return durationToString(this.videoAbuse.endAt) 38 return durationToString(this.abuse.endAt)
39 } 39 }
40 40
41 getPredefinedReasons () { 41 getPredefinedReasons () {
42 if (!this.videoAbuse.predefinedReasons) return [] 42 if (!this.abuse.predefinedReasons) return []
43 return this.videoAbuse.predefinedReasons.map(r => ({ 43 return this.abuse.predefinedReasons.map(r => ({
44 id: r, 44 id: r,
45 label: this.predefinedReasonsTranslations[r] 45 label: this.predefinedReasonsTranslations[r]
46 })) 46 }))
diff --git a/client/src/app/+admin/moderation/video-abuse-list/video-abuse-list.component.html b/client/src/app/+admin/moderation/abuse-list/abuse-list.component.html
index 64641b28a..167f32fe6 100644
--- a/client/src/app/+admin/moderation/video-abuse-list/video-abuse-list.component.html
+++ b/client/src/app/+admin/moderation/abuse-list/abuse-list.component.html
@@ -1,5 +1,5 @@
1<p-table 1<p-table
2 [value]="videoAbuses" [lazy]="true" [paginator]="totalRecords > 0" [totalRecords]="totalRecords" [rows]="rowsPerPage" [rowsPerPageOptions]="rowsPerPageOptions" 2 [value]="abuses" [lazy]="true" [paginator]="totalRecords > 0" [totalRecords]="totalRecords" [rows]="rowsPerPage" [rowsPerPageOptions]="rowsPerPageOptions"
3 [sortField]="sort.field" [sortOrder]="sort.order" (onLazyLoad)="loadLazy($event)" dataKey="id" [resizableColumns]="true" 3 [sortField]="sort.field" [sortOrder]="sort.order" (onLazyLoad)="loadLazy($event)" dataKey="id" [resizableColumns]="true"
4 [showCurrentPageReport]="true" i18n-currentPageReportTemplate 4 [showCurrentPageReport]="true" i18n-currentPageReportTemplate
5 currentPageReportTemplate="Showing {{'{first}'}} to {{'{last}'}} of {{'{totalRecords}'}} reports" 5 currentPageReportTemplate="Showing {{'{first}'}} to {{'{last}'}} of {{'{totalRecords}'}} reports"
@@ -16,11 +16,11 @@
16 16
17 <div role="menu" ngbDropdownMenu> 17 <div role="menu" ngbDropdownMenu>
18 <h6 class="dropdown-header" i18n>Advanced report filters</h6> 18 <h6 class="dropdown-header" i18n>Advanced report filters</h6>
19 <a [routerLink]="[ '/admin/moderation/video-abuses/list' ]" [queryParams]="{ 'search': 'state:pending' }" class="dropdown-item" i18n>Unsolved reports</a> 19 <a [routerLink]="[ '/admin/moderation/abuses/list' ]" [queryParams]="{ 'search': 'state:pending' }" class="dropdown-item" i18n>Unsolved reports</a>
20 <a [routerLink]="[ '/admin/moderation/video-abuses/list' ]" [queryParams]="{ 'search': 'state:accepted' }" class="dropdown-item" i18n>Accepted reports</a> 20 <a [routerLink]="[ '/admin/moderation/abuses/list' ]" [queryParams]="{ 'search': 'state:accepted' }" class="dropdown-item" i18n>Accepted reports</a>
21 <a [routerLink]="[ '/admin/moderation/video-abuses/list' ]" [queryParams]="{ 'search': 'state:rejected' }" class="dropdown-item" i18n>Refused reports</a> 21 <a [routerLink]="[ '/admin/moderation/abuses/list' ]" [queryParams]="{ 'search': 'state:rejected' }" class="dropdown-item" i18n>Refused reports</a>
22 <a [routerLink]="[ '/admin/moderation/video-abuses/list' ]" [queryParams]="{ 'search': 'videoIs:blacklisted' }" class="dropdown-item" i18n>Reports with blocked videos</a> 22 <a [routerLink]="[ '/admin/moderation/abuses/list' ]" [queryParams]="{ 'search': 'videoIs:blacklisted' }" class="dropdown-item" i18n>Reports with blocked videos</a>
23 <a [routerLink]="[ '/admin/moderation/video-abuses/list' ]" [queryParams]="{ 'search': 'videoIs:deleted' }" class="dropdown-item" i18n>Reports with deleted videos</a> 23 <a [routerLink]="[ '/admin/moderation/abuses/list' ]" [queryParams]="{ 'search': 'videoIs:deleted' }" class="dropdown-item" i18n>Reports with deleted videos</a>
24 </div> 24 </div>
25 </div> 25 </div>
26 <input 26 <input
@@ -45,91 +45,91 @@
45 </tr> 45 </tr>
46 </ng-template> 46 </ng-template>
47 47
48 <ng-template pTemplate="body" let-expanded="expanded" let-videoAbuse> 48 <ng-template pTemplate="body" let-expanded="expanded" let-abuse>
49 <tr> 49 <tr>
50 <td class="c-hand" [pRowToggler]="videoAbuse" i18n-ngbTooltip ngbTooltip="More information" placement="top-left" container="body"> 50 <td class="c-hand" [pRowToggler]="abuse" i18n-ngbTooltip ngbTooltip="More information" placement="top-left" container="body">
51 <span class="expander"> 51 <span class="expander">
52 <i [ngClass]="expanded ? 'glyphicon glyphicon-menu-down' : 'glyphicon glyphicon-menu-right'"></i> 52 <i [ngClass]="expanded ? 'glyphicon glyphicon-menu-down' : 'glyphicon glyphicon-menu-right'"></i>
53 </span> 53 </span>
54 </td> 54 </td>
55 55
56 <td> 56 <td>
57 <a [href]="videoAbuse.reporterAccount.url" i18n-title title="Open account in a new tab" target="_blank" rel="noopener noreferrer"> 57 <a [href]="abuse.reporterAccount.url" i18n-title title="Open account in a new tab" target="_blank" rel="noopener noreferrer">
58 <div class="chip two-lines"> 58 <div class="chip two-lines">
59 <img 59 <img
60 class="avatar" 60 class="avatar"
61 [src]="videoAbuse.reporterAccount.avatar?.path" 61 [src]="abuse.reporterAccount.avatar?.path"
62 (error)="switchToDefaultAvatar($event)" 62 (error)="switchToDefaultAvatar($event)"
63 alt="Avatar" 63 alt="Avatar"
64 > 64 >
65 <div> 65 <div>
66 {{ videoAbuse.reporterAccount.displayName }} 66 {{ abuse.reporterAccount.displayName }}
67 <span class="text-muted">{{ videoAbuse.reporterAccount.nameWithHost }}</span> 67 <span class="text-muted">{{ abuse.reporterAccount.nameWithHost }}</span>
68 </div> 68 </div>
69 </div> 69 </div>
70 </a> 70 </a>
71 </td> 71 </td>
72 72
73 <td *ngIf="!videoAbuse.video.deleted"> 73 <td *ngIf="!abuse.video.deleted">
74 <a [href]="getVideoUrl(videoAbuse)" class="video-table-video-link" [title]="videoAbuse.video.name" target="_blank" rel="noopener noreferrer"> 74 <a [href]="getVideoUrl(abuse)" class="video-table-video-link" [title]="abuse.video.name" target="_blank" rel="noopener noreferrer">
75 <div class="video-table-video"> 75 <div class="video-table-video">
76 <div class="video-table-video-image"> 76 <div class="video-table-video-image">
77 <img [src]="videoAbuse.video.thumbnailPath"> 77 <img [src]="abuse.video.thumbnailPath">
78 <span 78 <span
79 class="video-table-video-image-label" *ngIf="videoAbuse.count > 1" 79 class="video-table-video-image-label" *ngIf="abuse.count > 1"
80 i18n-title title="This video has been reported multiple times." 80 i18n-title title="This video has been reported multiple times."
81 > 81 >
82 {{ videoAbuse.nth }}/{{ videoAbuse.count }} 82 {{ abuse.nth }}/{{ abuse.count }}
83 </span> 83 </span>
84 </div> 84 </div>
85 <div class="video-table-video-text"> 85 <div class="video-table-video-text">
86 <div> 86 <div>
87 <span *ngIf="!videoAbuse.video.blacklisted" class="glyphicon glyphicon-new-window"></span> 87 <span *ngIf="!abuse.video.blacklisted" class="glyphicon glyphicon-new-window"></span>
88 <span *ngIf="videoAbuse.video.blacklisted" i18n-title title="The video was blocked" class="glyphicon glyphicon-ban-circle"></span> 88 <span *ngIf="abuse.video.blacklisted" i18n-title title="The video was blocked" class="glyphicon glyphicon-ban-circle"></span>
89 {{ videoAbuse.video.name }} 89 {{ abuse.video.name }}
90 </div> 90 </div>
91 <div class="text-muted" i18n>by {{ videoAbuse.video.channel?.displayName }} on {{ videoAbuse.video.channel?.host }} </div> 91 <div class="text-muted" i18n>by {{ abuse.video.channel?.displayName }} on {{ abuse.video.channel?.host }} </div>
92 </div> 92 </div>
93 </div> 93 </div>
94 </a> 94 </a>
95 </td> 95 </td>
96 96
97 <td *ngIf="videoAbuse.video.deleted" class="c-hand" [pRowToggler]="videoAbuse"> 97 <td *ngIf="abuse.video.deleted" class="c-hand" [pRowToggler]="abuse">
98 <div class="video-table-video" i18n-title title="Video was deleted"> 98 <div class="video-table-video" i18n-title title="Video was deleted">
99 <div class="video-table-video-image"> 99 <div class="video-table-video-image">
100 <span i18n>Deleted</span> 100 <span i18n>Deleted</span>
101 </div> 101 </div>
102 <div class="video-table-video-text"> 102 <div class="video-table-video-text">
103 <div> 103 <div>
104 {{ videoAbuse.video.name }} 104 {{ abuse.video.name }}
105 <span class="glyphicon glyphicon-trash"></span> 105 <span class="glyphicon glyphicon-trash"></span>
106 </div> 106 </div>
107 <div class="text-muted" i18n>by {{ videoAbuse.video.channel?.displayName }} on {{ videoAbuse.video.channel?.host }} </div> 107 <div class="text-muted" i18n>by {{ abuse.video.channel?.displayName }} on {{ abuse.video.channel?.host }} </div>
108 </div> 108 </div>
109 </div> 109 </div>
110 </td> 110 </td>
111 111
112 <td class="c-hand" [pRowToggler]="videoAbuse">{{ videoAbuse.createdAt | date: 'short' }}</td> 112 <td class="c-hand" [pRowToggler]="abuse">{{ abuse.createdAt | date: 'short' }}</td>
113 113
114 <td class="c-hand video-abuse-states" [pRowToggler]="videoAbuse"> 114 <td class="c-hand video-abuse-states" [pRowToggler]="abuse">
115 <span *ngIf="isVideoAbuseAccepted(videoAbuse)" [title]="videoAbuse.state.label" class="glyphicon glyphicon-ok"></span> 115 <span *ngIf="isAbuseAccepted(abuse)" [title]="abuse.state.label" class="glyphicon glyphicon-ok"></span>
116 <span *ngIf="isVideoAbuseRejected(videoAbuse)" [title]="videoAbuse.state.label" class="glyphicon glyphicon-remove"></span> 116 <span *ngIf="isAbuseRejected(abuse)" [title]="abuse.state.label" class="glyphicon glyphicon-remove"></span>
117 <span *ngIf="videoAbuse.moderationComment" container="body" placement="left auto" [ngbTooltip]="videoAbuse.moderationComment" class="glyphicon glyphicon-comment"></span> 117 <span *ngIf="abuse.moderationComment" container="body" placement="left auto" [ngbTooltip]="abuse.moderationComment" class="glyphicon glyphicon-comment"></span>
118 </td> 118 </td>
119 119
120 <td class="action-cell"> 120 <td class="action-cell">
121 <my-action-dropdown 121 <my-action-dropdown
122 [ngClass]="{ 'show': expanded }" placement="bottom-right top-right left auto" container="body" 122 [ngClass]="{ 'show': expanded }" placement="bottom-right top-right left auto" container="body"
123 i18n-label label="Actions" [actions]="videoAbuseActions" [entry]="videoAbuse" 123 i18n-label label="Actions" [actions]="abuseActions" [entry]="abuse"
124 ></my-action-dropdown> 124 ></my-action-dropdown>
125 </td> 125 </td>
126 </tr> 126 </tr>
127 </ng-template> 127 </ng-template>
128 128
129 <ng-template pTemplate="rowexpansion" let-videoAbuse> 129 <ng-template pTemplate="rowexpansion" let-abuse>
130 <tr> 130 <tr>
131 <td class="expand-cell" colspan="6"> 131 <td class="expand-cell" colspan="6">
132 <my-video-abuse-details [videoAbuse]="videoAbuse"></my-video-abuse-details> 132 <my-abuse-details [abuse]="abuse"></my-abuse-details>
133 </td> 133 </td>
134 </tr> 134 </tr>
135 </ng-template> 135 </ng-template>
diff --git a/client/src/app/+admin/moderation/video-abuse-list/video-abuse-list.component.scss b/client/src/app/+admin/moderation/abuse-list/abuse-list.component.scss
index 8eee15b64..8eee15b64 100644
--- a/client/src/app/+admin/moderation/video-abuse-list/video-abuse-list.component.scss
+++ b/client/src/app/+admin/moderation/abuse-list/abuse-list.component.scss
diff --git a/client/src/app/+admin/moderation/video-abuse-list/video-abuse-list.component.ts b/client/src/app/+admin/moderation/abuse-list/abuse-list.component.ts
index 409dd42c7..427ec4d5d 100644
--- a/client/src/app/+admin/moderation/video-abuse-list/video-abuse-list.component.ts
+++ b/client/src/app/+admin/moderation/abuse-list/abuse-list.component.ts
@@ -1,5 +1,4 @@
1import { SortMeta } from 'primeng/api' 1import { SortMeta } from 'primeng/api'
2import { filter } from 'rxjs/operators'
3import { buildVideoEmbed, buildVideoLink } from 'src/assets/player/utils' 2import { buildVideoEmbed, buildVideoLink } from 'src/assets/player/utils'
4import { environment } from 'src/environments/environment' 3import { environment } from 'src/environments/environment'
5import { AfterViewInit, Component, OnInit, ViewChild } from '@angular/core' 4import { AfterViewInit, Component, OnInit, ViewChild } from '@angular/core'
@@ -7,43 +6,45 @@ import { DomSanitizer } from '@angular/platform-browser'
7import { ActivatedRoute, Params, Router } from '@angular/router' 6import { ActivatedRoute, Params, Router } from '@angular/router'
8import { ConfirmService, MarkdownService, Notifier, RestPagination, RestTable } from '@app/core' 7import { ConfirmService, MarkdownService, Notifier, RestPagination, RestTable } from '@app/core'
9import { Account, Actor, DropdownAction, Video, VideoService } from '@app/shared/shared-main' 8import { Account, Actor, DropdownAction, Video, VideoService } from '@app/shared/shared-main'
10import { BlocklistService, VideoAbuseService, VideoBlockService } from '@app/shared/shared-moderation' 9import { AbuseService, BlocklistService, VideoBlockService } from '@app/shared/shared-moderation'
11import { I18n } from '@ngx-translate/i18n-polyfill' 10import { I18n } from '@ngx-translate/i18n-polyfill'
12import { VideoAbuse, VideoAbuseState } from '@shared/models' 11import { Abuse, AbuseState } from '@shared/models'
13import { ModerationCommentModalComponent } from './moderation-comment-modal.component' 12import { ModerationCommentModalComponent } from './moderation-comment-modal.component'
14 13
15export type ProcessedVideoAbuse = VideoAbuse & { 14export type ProcessedAbuse = Abuse & {
16 moderationCommentHtml?: string, 15 moderationCommentHtml?: string,
17 reasonHtml?: string 16 reasonHtml?: string
18 embedHtml?: string 17 embedHtml?: string
19 updatedAt?: Date 18 updatedAt?: Date
19
20 // override bare server-side definitions with rich client-side definitions 20 // override bare server-side definitions with rich client-side definitions
21 reporterAccount: Account 21 reporterAccount: Account
22 video: VideoAbuse['video'] & { 22
23 channel: VideoAbuse['video']['channel'] & { 23 video: Abuse['video'] & {
24 channel: Abuse['video']['channel'] & {
24 ownerAccount: Account 25 ownerAccount: Account
25 } 26 }
26 } 27 }
27} 28}
28 29
29@Component({ 30@Component({
30 selector: 'my-video-abuse-list', 31 selector: 'my-abuse-list',
31 templateUrl: './video-abuse-list.component.html', 32 templateUrl: './abuse-list.component.html',
32 styleUrls: [ '../moderation.component.scss', './video-abuse-list.component.scss' ] 33 styleUrls: [ '../moderation.component.scss', './abuse-list.component.scss' ]
33}) 34})
34export class VideoAbuseListComponent extends RestTable implements OnInit, AfterViewInit { 35export class AbuseListComponent extends RestTable implements OnInit, AfterViewInit {
35 @ViewChild('moderationCommentModal', { static: true }) moderationCommentModal: ModerationCommentModalComponent 36 @ViewChild('moderationCommentModal', { static: true }) moderationCommentModal: ModerationCommentModalComponent
36 37
37 videoAbuses: ProcessedVideoAbuse[] = [] 38 abuses: ProcessedAbuse[] = []
38 totalRecords = 0 39 totalRecords = 0
39 sort: SortMeta = { field: 'createdAt', order: 1 } 40 sort: SortMeta = { field: 'createdAt', order: 1 }
40 pagination: RestPagination = { count: this.rowsPerPage, start: 0 } 41 pagination: RestPagination = { count: this.rowsPerPage, start: 0 }
41 42
42 videoAbuseActions: DropdownAction<VideoAbuse>[][] = [] 43 abuseActions: DropdownAction<Abuse>[][] = []
43 44
44 constructor ( 45 constructor (
45 private notifier: Notifier, 46 private notifier: Notifier,
46 private videoAbuseService: VideoAbuseService, 47 private abuseService: AbuseService,
47 private blocklistService: BlocklistService, 48 private blocklistService: BlocklistService,
48 private videoService: VideoService, 49 private videoService: VideoService,
49 private videoBlocklistService: VideoBlockService, 50 private videoBlocklistService: VideoBlockService,
@@ -56,7 +57,7 @@ export class VideoAbuseListComponent extends RestTable implements OnInit, AfterV
56 ) { 57 ) {
57 super() 58 super()
58 59
59 this.videoAbuseActions = [ 60 this.abuseActions = [
60 [ 61 [
61 { 62 {
62 label: this.i18n('Internal actions'), 63 label: this.i18n('Internal actions'),
@@ -64,45 +65,45 @@ export class VideoAbuseListComponent extends RestTable implements OnInit, AfterV
64 }, 65 },
65 { 66 {
66 label: this.i18n('Delete report'), 67 label: this.i18n('Delete report'),
67 handler: videoAbuse => this.removeVideoAbuse(videoAbuse) 68 handler: abuse => this.removeAbuse(abuse)
68 }, 69 },
69 { 70 {
70 label: this.i18n('Add note'), 71 label: this.i18n('Add note'),
71 handler: videoAbuse => this.openModerationCommentModal(videoAbuse), 72 handler: abuse => this.openModerationCommentModal(abuse),
72 isDisplayed: videoAbuse => !videoAbuse.moderationComment 73 isDisplayed: abuse => !abuse.moderationComment
73 }, 74 },
74 { 75 {
75 label: this.i18n('Update note'), 76 label: this.i18n('Update note'),
76 handler: videoAbuse => this.openModerationCommentModal(videoAbuse), 77 handler: abuse => this.openModerationCommentModal(abuse),
77 isDisplayed: videoAbuse => !!videoAbuse.moderationComment 78 isDisplayed: abuse => !!abuse.moderationComment
78 }, 79 },
79 { 80 {
80 label: this.i18n('Mark as accepted'), 81 label: this.i18n('Mark as accepted'),
81 handler: videoAbuse => this.updateVideoAbuseState(videoAbuse, VideoAbuseState.ACCEPTED), 82 handler: abuse => this.updateAbuseState(abuse, AbuseState.ACCEPTED),
82 isDisplayed: videoAbuse => !this.isVideoAbuseAccepted(videoAbuse) 83 isDisplayed: abuse => !this.isAbuseAccepted(abuse)
83 }, 84 },
84 { 85 {
85 label: this.i18n('Mark as rejected'), 86 label: this.i18n('Mark as rejected'),
86 handler: videoAbuse => this.updateVideoAbuseState(videoAbuse, VideoAbuseState.REJECTED), 87 handler: abuse => this.updateAbuseState(abuse, AbuseState.REJECTED),
87 isDisplayed: videoAbuse => !this.isVideoAbuseRejected(videoAbuse) 88 isDisplayed: abuse => !this.isAbuseRejected(abuse)
88 } 89 }
89 ], 90 ],
90 [ 91 [
91 { 92 {
92 label: this.i18n('Actions for the video'), 93 label: this.i18n('Actions for the video'),
93 isHeader: true, 94 isHeader: true,
94 isDisplayed: videoAbuse => !videoAbuse.video.deleted 95 isDisplayed: abuse => !abuse.video.deleted
95 }, 96 },
96 { 97 {
97 label: this.i18n('Block video'), 98 label: this.i18n('Block video'),
98 isDisplayed: videoAbuse => !videoAbuse.video.deleted && !videoAbuse.video.blacklisted, 99 isDisplayed: abuse => !abuse.video.deleted && !abuse.video.blacklisted,
99 handler: videoAbuse => { 100 handler: abuse => {
100 this.videoBlocklistService.blockVideo(videoAbuse.video.id, undefined, true) 101 this.videoBlocklistService.blockVideo(abuse.video.id, undefined, true)
101 .subscribe( 102 .subscribe(
102 () => { 103 () => {
103 this.notifier.success(this.i18n('Video blocked.')) 104 this.notifier.success(this.i18n('Video blocked.'))
104 105
105 this.updateVideoAbuseState(videoAbuse, VideoAbuseState.ACCEPTED) 106 this.updateAbuseState(abuse, AbuseState.ACCEPTED)
106 }, 107 },
107 108
108 err => this.notifier.error(err.message) 109 err => this.notifier.error(err.message)
@@ -111,14 +112,14 @@ export class VideoAbuseListComponent extends RestTable implements OnInit, AfterV
111 }, 112 },
112 { 113 {
113 label: this.i18n('Unblock video'), 114 label: this.i18n('Unblock video'),
114 isDisplayed: videoAbuse => !videoAbuse.video.deleted && videoAbuse.video.blacklisted, 115 isDisplayed: abuse => !abuse.video.deleted && abuse.video.blacklisted,
115 handler: videoAbuse => { 116 handler: abuse => {
116 this.videoBlocklistService.unblockVideo(videoAbuse.video.id) 117 this.videoBlocklistService.unblockVideo(abuse.video.id)
117 .subscribe( 118 .subscribe(
118 () => { 119 () => {
119 this.notifier.success(this.i18n('Video unblocked.')) 120 this.notifier.success(this.i18n('Video unblocked.'))
120 121
121 this.updateVideoAbuseState(videoAbuse, VideoAbuseState.ACCEPTED) 122 this.updateAbuseState(abuse, AbuseState.ACCEPTED)
122 }, 123 },
123 124
124 err => this.notifier.error(err.message) 125 err => this.notifier.error(err.message)
@@ -127,20 +128,20 @@ export class VideoAbuseListComponent extends RestTable implements OnInit, AfterV
127 }, 128 },
128 { 129 {
129 label: this.i18n('Delete video'), 130 label: this.i18n('Delete video'),
130 isDisplayed: videoAbuse => !videoAbuse.video.deleted, 131 isDisplayed: abuse => !abuse.video.deleted,
131 handler: async videoAbuse => { 132 handler: async abuse => {
132 const res = await this.confirmService.confirm( 133 const res = await this.confirmService.confirm(
133 this.i18n('Do you really want to delete this video?'), 134 this.i18n('Do you really want to delete this video?'),
134 this.i18n('Delete') 135 this.i18n('Delete')
135 ) 136 )
136 if (res === false) return 137 if (res === false) return
137 138
138 this.videoService.removeVideo(videoAbuse.video.id) 139 this.videoService.removeVideo(abuse.video.id)
139 .subscribe( 140 .subscribe(
140 () => { 141 () => {
141 this.notifier.success(this.i18n('Video deleted.')) 142 this.notifier.success(this.i18n('Video deleted.'))
142 143
143 this.updateVideoAbuseState(videoAbuse, VideoAbuseState.ACCEPTED) 144 this.updateAbuseState(abuse, AbuseState.ACCEPTED)
144 }, 145 },
145 146
146 err => this.notifier.error(err.message) 147 err => this.notifier.error(err.message)
@@ -155,8 +156,8 @@ export class VideoAbuseListComponent extends RestTable implements OnInit, AfterV
155 }, 156 },
156 { 157 {
157 label: this.i18n('Mute reporter'), 158 label: this.i18n('Mute reporter'),
158 handler: async videoAbuse => { 159 handler: async abuse => {
159 const account = videoAbuse.reporterAccount as Account 160 const account = abuse.reporterAccount as Account
160 161
161 this.blocklistService.blockAccountByInstance(account) 162 this.blocklistService.blockAccountByInstance(account)
162 .subscribe( 163 .subscribe(
@@ -174,13 +175,13 @@ export class VideoAbuseListComponent extends RestTable implements OnInit, AfterV
174 }, 175 },
175 { 176 {
176 label: this.i18n('Mute server'), 177 label: this.i18n('Mute server'),
177 isDisplayed: videoAbuse => !videoAbuse.reporterAccount.userId, 178 isDisplayed: abuse => !abuse.reporterAccount.userId,
178 handler: async videoAbuse => { 179 handler: async abuse => {
179 this.blocklistService.blockServerByInstance(videoAbuse.reporterAccount.host) 180 this.blocklistService.blockServerByInstance(abuse.reporterAccount.host)
180 .subscribe( 181 .subscribe(
181 () => { 182 () => {
182 this.notifier.success( 183 this.notifier.success(
183 this.i18n('Server {{host}} muted by the instance.', { host: videoAbuse.reporterAccount.host }) 184 this.i18n('Server {{host}} muted by the instance.', { host: abuse.reporterAccount.host })
184 ) 185 )
185 }, 186 },
186 187
@@ -209,11 +210,11 @@ export class VideoAbuseListComponent extends RestTable implements OnInit, AfterV
209 } 210 }
210 211
211 getIdentifier () { 212 getIdentifier () {
212 return 'VideoAbuseListComponent' 213 return 'AbuseListComponent'
213 } 214 }
214 215
215 openModerationCommentModal (videoAbuse: VideoAbuse) { 216 openModerationCommentModal (abuse: Abuse) {
216 this.moderationCommentModal.openModal(videoAbuse) 217 this.moderationCommentModal.openModal(abuse)
217 } 218 }
218 219
219 onModerationCommentUpdated () { 220 onModerationCommentUpdated () {
@@ -240,26 +241,26 @@ export class VideoAbuseListComponent extends RestTable implements OnInit, AfterV
240 } 241 }
241 /* END Table filter functions */ 242 /* END Table filter functions */
242 243
243 isVideoAbuseAccepted (videoAbuse: VideoAbuse) { 244 isAbuseAccepted (abuse: Abuse) {
244 return videoAbuse.state.id === VideoAbuseState.ACCEPTED 245 return abuse.state.id === AbuseState.ACCEPTED
245 } 246 }
246 247
247 isVideoAbuseRejected (videoAbuse: VideoAbuse) { 248 isAbuseRejected (abuse: Abuse) {
248 return videoAbuse.state.id === VideoAbuseState.REJECTED 249 return abuse.state.id === AbuseState.REJECTED
249 } 250 }
250 251
251 getVideoUrl (videoAbuse: VideoAbuse) { 252 getVideoUrl (abuse: Abuse) {
252 return Video.buildClientUrl(videoAbuse.video.uuid) 253 return Video.buildClientUrl(abuse.video.uuid)
253 } 254 }
254 255
255 getVideoEmbed (videoAbuse: VideoAbuse) { 256 getVideoEmbed (abuse: Abuse) {
256 return buildVideoEmbed( 257 return buildVideoEmbed(
257 buildVideoLink({ 258 buildVideoLink({
258 baseUrl: `${environment.embedUrl}/videos/embed/${videoAbuse.video.uuid}`, 259 baseUrl: `${environment.embedUrl}/videos/embed/${abuse.video.uuid}`,
259 title: false, 260 title: false,
260 warningTitle: false, 261 warningTitle: false,
261 startTime: videoAbuse.startAt, 262 startTime: abuse.startAt,
262 stopTime: videoAbuse.endAt 263 stopTime: abuse.endAt
263 }) 264 })
264 ) 265 )
265 } 266 }
@@ -268,11 +269,11 @@ export class VideoAbuseListComponent extends RestTable implements OnInit, AfterV
268 ($event.target as HTMLImageElement).src = Actor.GET_DEFAULT_AVATAR_URL() 269 ($event.target as HTMLImageElement).src = Actor.GET_DEFAULT_AVATAR_URL()
269 } 270 }
270 271
271 async removeVideoAbuse (videoAbuse: VideoAbuse) { 272 async removeAbuse (abuse: Abuse) {
272 const res = await this.confirmService.confirm(this.i18n('Do you really want to delete this abuse report?'), this.i18n('Delete')) 273 const res = await this.confirmService.confirm(this.i18n('Do you really want to delete this abuse report?'), this.i18n('Delete'))
273 if (res === false) return 274 if (res === false) return
274 275
275 this.videoAbuseService.removeVideoAbuse(videoAbuse).subscribe( 276 this.abuseService.removeAbuse(abuse).subscribe(
276 () => { 277 () => {
277 this.notifier.success(this.i18n('Abuse deleted.')) 278 this.notifier.success(this.i18n('Abuse deleted.'))
278 this.loadData() 279 this.loadData()
@@ -282,8 +283,8 @@ export class VideoAbuseListComponent extends RestTable implements OnInit, AfterV
282 ) 283 )
283 } 284 }
284 285
285 updateVideoAbuseState (videoAbuse: VideoAbuse, state: VideoAbuseState) { 286 updateAbuseState (abuse: Abuse, state: AbuseState) {
286 this.videoAbuseService.updateVideoAbuse(videoAbuse, { state }) 287 this.abuseService.updateAbuse(abuse, { state })
287 .subscribe( 288 .subscribe(
288 () => this.loadData(), 289 () => this.loadData(),
289 290
@@ -292,14 +293,14 @@ export class VideoAbuseListComponent extends RestTable implements OnInit, AfterV
292 } 293 }
293 294
294 protected loadData () { 295 protected loadData () {
295 return this.videoAbuseService.getVideoAbuses({ 296 return this.abuseService.getAbuses({
296 pagination: this.pagination, 297 pagination: this.pagination,
297 sort: this.sort, 298 sort: this.sort,
298 search: this.search 299 search: this.search
299 }).subscribe( 300 }).subscribe(
300 async resultList => { 301 async resultList => {
301 this.totalRecords = resultList.total 302 this.totalRecords = resultList.total
302 const videoAbuses = [] 303 const abuses = []
303 304
304 for (const abuse of resultList.data) { 305 for (const abuse of resultList.data) {
305 Object.assign(abuse, { 306 Object.assign(abuse, {
@@ -312,10 +313,10 @@ export class VideoAbuseListComponent extends RestTable implements OnInit, AfterV
312 if (abuse.video.channel?.ownerAccount) abuse.video.channel.ownerAccount = new Account(abuse.video.channel.ownerAccount) 313 if (abuse.video.channel?.ownerAccount) abuse.video.channel.ownerAccount = new Account(abuse.video.channel.ownerAccount)
313 if (abuse.updatedAt === abuse.createdAt) delete abuse.updatedAt 314 if (abuse.updatedAt === abuse.createdAt) delete abuse.updatedAt
314 315
315 videoAbuses.push(abuse as ProcessedVideoAbuse) 316 abuses.push(abuse as ProcessedAbuse)
316 } 317 }
317 318
318 this.videoAbuses = videoAbuses 319 this.abuses = abuses
319 }, 320 },
320 321
321 err => this.notifier.error(err.message) 322 err => this.notifier.error(err.message)
diff --git a/client/src/app/+admin/moderation/abuse-list/index.ts b/client/src/app/+admin/moderation/abuse-list/index.ts
new file mode 100644
index 000000000..c6037dab4
--- /dev/null
+++ b/client/src/app/+admin/moderation/abuse-list/index.ts
@@ -0,0 +1,3 @@
1export * from './abuse-details.component'
2export * from './abuse-list.component'
3export * from './moderation-comment-modal.component'
diff --git a/client/src/app/+admin/moderation/video-abuse-list/moderation-comment-modal.component.html b/client/src/app/+admin/moderation/abuse-list/moderation-comment-modal.component.html
index 8082e93f4..8082e93f4 100644
--- a/client/src/app/+admin/moderation/video-abuse-list/moderation-comment-modal.component.html
+++ b/client/src/app/+admin/moderation/abuse-list/moderation-comment-modal.component.html
diff --git a/client/src/app/+admin/moderation/video-abuse-list/moderation-comment-modal.component.scss b/client/src/app/+admin/moderation/abuse-list/moderation-comment-modal.component.scss
index afcdb9a16..afcdb9a16 100644
--- a/client/src/app/+admin/moderation/video-abuse-list/moderation-comment-modal.component.scss
+++ b/client/src/app/+admin/moderation/abuse-list/moderation-comment-modal.component.scss
diff --git a/client/src/app/+admin/moderation/video-abuse-list/moderation-comment-modal.component.ts b/client/src/app/+admin/moderation/abuse-list/moderation-comment-modal.component.ts
index 3cd763ca4..23738f9cd 100644
--- a/client/src/app/+admin/moderation/video-abuse-list/moderation-comment-modal.component.ts
+++ b/client/src/app/+admin/moderation/abuse-list/moderation-comment-modal.component.ts
@@ -1,11 +1,11 @@
1import { Component, EventEmitter, OnInit, Output, ViewChild } from '@angular/core' 1import { Component, EventEmitter, OnInit, Output, ViewChild } from '@angular/core'
2import { Notifier } from '@app/core' 2import { Notifier } from '@app/core'
3import { FormReactive, FormValidatorService, VideoAbuseValidatorsService } from '@app/shared/shared-forms' 3import { FormReactive, FormValidatorService, AbuseValidatorsService } from '@app/shared/shared-forms'
4import { VideoAbuseService } from '@app/shared/shared-moderation' 4import { AbuseService } from '@app/shared/shared-moderation'
5import { NgbModal } from '@ng-bootstrap/ng-bootstrap' 5import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
6import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap/modal/modal-ref' 6import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap/modal/modal-ref'
7import { I18n } from '@ngx-translate/i18n-polyfill' 7import { I18n } from '@ngx-translate/i18n-polyfill'
8import { VideoAbuse } from '@shared/models' 8import { Abuse } from '@shared/models'
9 9
10@Component({ 10@Component({
11 selector: 'my-moderation-comment-modal', 11 selector: 'my-moderation-comment-modal',
@@ -16,15 +16,15 @@ 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: VideoAbuse 19 private abuseToComment: Abuse
20 private openedModal: NgbModalRef 20 private openedModal: NgbModalRef
21 21
22 constructor ( 22 constructor (
23 protected formValidatorService: FormValidatorService, 23 protected formValidatorService: FormValidatorService,
24 private modalService: NgbModal, 24 private modalService: NgbModal,
25 private notifier: Notifier, 25 private notifier: Notifier,
26 private videoAbuseService: VideoAbuseService, 26 private abuseService: AbuseService,
27 private videoAbuseValidatorsService: VideoAbuseValidatorsService, 27 private abuseValidatorsService: AbuseValidatorsService,
28 private i18n: I18n 28 private i18n: I18n
29 ) { 29 ) {
30 super() 30 super()
@@ -32,11 +32,11 @@ export class ModerationCommentModalComponent extends FormReactive implements OnI
32 32
33 ngOnInit () { 33 ngOnInit () {
34 this.buildForm({ 34 this.buildForm({
35 moderationComment: this.videoAbuseValidatorsService.VIDEO_ABUSE_MODERATION_COMMENT 35 moderationComment: this.abuseValidatorsService.ABUSE_MODERATION_COMMENT
36 }) 36 })
37 } 37 }
38 38
39 openModal (abuseToComment: VideoAbuse) { 39 openModal (abuseToComment: Abuse) {
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
@@ -54,7 +54,7 @@ export class ModerationCommentModalComponent extends FormReactive implements OnI
54 async banUser () { 54 async banUser () {
55 const moderationComment: string = this.form.value[ 'moderationComment' ] 55 const moderationComment: string = this.form.value[ 'moderationComment' ]
56 56
57 this.videoAbuseService.updateVideoAbuse(this.abuseToComment, { moderationComment }) 57 this.abuseService.updateAbuse(this.abuseToComment, { moderationComment })
58 .subscribe( 58 .subscribe(
59 () => { 59 () => {
60 this.notifier.success(this.i18n('Comment updated.')) 60 this.notifier.success(this.i18n('Comment updated.'))
diff --git a/client/src/app/+admin/moderation/index.ts b/client/src/app/+admin/moderation/index.ts
index 16249236c..53e4bc991 100644
--- a/client/src/app/+admin/moderation/index.ts
+++ b/client/src/app/+admin/moderation/index.ts
@@ -1,5 +1,5 @@
1export * from './abuse-list'
1export * from './instance-blocklist' 2export * from './instance-blocklist'
2export * from './video-abuse-list'
3export * from './video-block-list' 3export * from './video-block-list'
4export * from './moderation.component' 4export * from './moderation.component'
5export * from './moderation.routes' 5export * from './moderation.routes'
diff --git a/client/src/app/+admin/moderation/moderation.routes.ts b/client/src/app/+admin/moderation/moderation.routes.ts
index cd837bcb9..1e207e5e8 100644
--- a/client/src/app/+admin/moderation/moderation.routes.ts
+++ b/client/src/app/+admin/moderation/moderation.routes.ts
@@ -1,7 +1,7 @@
1import { Routes } from '@angular/router' 1import { Routes } from '@angular/router'
2import { InstanceAccountBlocklistComponent, InstanceServerBlocklistComponent } from '@app/+admin/moderation/instance-blocklist' 2import { InstanceAccountBlocklistComponent, InstanceServerBlocklistComponent } from '@app/+admin/moderation/instance-blocklist'
3import { ModerationComponent } from '@app/+admin/moderation/moderation.component' 3import { ModerationComponent } from '@app/+admin/moderation/moderation.component'
4import { VideoAbuseListComponent } from '@app/+admin/moderation/video-abuse-list' 4import { AbuseListComponent } from '@app/+admin/moderation/abuse-list'
5import { VideoBlockListComponent } from '@app/+admin/moderation/video-block-list' 5import { VideoBlockListComponent } from '@app/+admin/moderation/video-block-list'
6import { UserRightGuard } from '@app/core' 6import { UserRightGuard } from '@app/core'
7import { UserRight } from '@shared/models' 7import { UserRight } from '@shared/models'
@@ -13,20 +13,25 @@ export const ModerationRoutes: Routes = [
13 children: [ 13 children: [
14 { 14 {
15 path: '', 15 path: '',
16 redirectTo: 'video-abuses/list', 16 redirectTo: 'abuses/list',
17 pathMatch: 'full' 17 pathMatch: 'full'
18 }, 18 },
19 { 19 {
20 path: 'video-abuses', 20 path: 'video-abuses',
21 redirectTo: 'video-abuses/list', 21 redirectTo: 'abuses/list',
22 pathMatch: 'full' 22 pathMatch: 'full'
23 }, 23 },
24 { 24 {
25 path: 'video-abuses/list', 25 path: 'video-abuses/list',
26 component: VideoAbuseListComponent, 26 redirectTo: 'abuses/list',
27 pathMatch: 'full'
28 },
29 {
30 path: 'abuses/list',
31 component: AbuseListComponent,
27 canActivate: [ UserRightGuard ], 32 canActivate: [ UserRightGuard ],
28 data: { 33 data: {
29 userRight: UserRight.MANAGE_VIDEO_ABUSES, 34 userRight: UserRight.MANAGE_ABUSES,
30 meta: { 35 meta: {
31 title: 'Video reports' 36 title: 'Video reports'
32 } 37 }
diff --git a/client/src/app/+admin/moderation/video-abuse-list/index.ts b/client/src/app/+admin/moderation/video-abuse-list/index.ts
deleted file mode 100644
index da7176e52..000000000
--- a/client/src/app/+admin/moderation/video-abuse-list/index.ts
+++ /dev/null
@@ -1,2 +0,0 @@
1export * from './video-abuse-list.component'
2export * from './moderation-comment-modal.component'
diff --git a/client/src/app/+admin/moderation/video-abuse-list/video-abuse-details.component.html b/client/src/app/+admin/moderation/video-abuse-list/video-abuse-details.component.html
deleted file mode 100644
index ec808cdb8..000000000
--- a/client/src/app/+admin/moderation/video-abuse-list/video-abuse-details.component.html
+++ /dev/null
@@ -1,93 +0,0 @@
1<div class="d-flex moderation-expanded">
2 <!-- report left part (report details) -->
3 <div class="col-8">
4
5 <!-- report metadata -->
6 <div class="d-flex">
7 <span class="col-3 moderation-expanded-label" i18n>Reporter</span>
8 <span class="col-9 moderation-expanded-text">
9 <a [routerLink]="[ '/admin/moderation/video-abuses/list' ]" [queryParams]="{ 'search': 'reporter:&quot;' + videoAbuse.reporterAccount.displayName + '&quot;' }" class="chip">
10 <img
11 class="avatar"
12 [src]="videoAbuse.reporterAccount.avatar?.path"
13 (error)="switchToDefaultAvatar($event)"
14 alt="Avatar"
15 >
16 <div>
17 <span class="text-muted">{{ videoAbuse.reporterAccount.nameWithHost }}</span>
18 </div>
19 </a>
20 <a [routerLink]="[ '/admin/moderation/video-abuses/list' ]" [queryParams]="{ 'search': 'reporter:&quot;' + videoAbuse.reporterAccount.displayName + '&quot;' }" class="ml-auto text-muted video-details-links" i18n>
21 {videoAbuse.countReportsForReporter, plural, =1 {1 report} other {{{ videoAbuse.countReportsForReporter }} reports}}<span class="ml-1 glyphicon glyphicon-flag"></span>
22 </a>
23 </span>
24 </div>
25
26 <div class="d-flex">
27 <span class="col-3 moderation-expanded-label" i18n>Reportee</span>
28 <span class="col-9 moderation-expanded-text">
29 <a [routerLink]="[ '/admin/moderation/video-abuses/list' ]" [queryParams]="{ 'search': 'reportee:&quot;' +videoAbuse.video.channel.ownerAccount.displayName + '&quot;' }" class="chip">
30 <img
31 class="avatar"
32 [src]="videoAbuse.video.channel.ownerAccount?.avatar?.path"
33 (error)="switchToDefaultAvatar($event)"
34 alt="Avatar"
35 >
36 <div>
37 <span class="text-muted">{{ videoAbuse.video.channel.ownerAccount ? videoAbuse.video.channel.ownerAccount.nameWithHost : '' }}</span>
38 </div>
39 </a>
40 <a [routerLink]="[ '/admin/moderation/video-abuses/list' ]" [queryParams]="{ 'search': 'reportee:&quot;' +videoAbuse.video.channel.ownerAccount.displayName + '&quot;' }" class="ml-auto text-muted video-details-links" i18n>
41 {videoAbuse.countReportsForReportee, plural, =1 {1 report} other {{{ videoAbuse.countReportsForReportee }} reports}}<span class="ml-1 glyphicon glyphicon-flag"></span>
42 </a>
43 </span>
44 </div>
45
46 <div class="d-flex" *ngIf="videoAbuse.updatedAt">
47 <span class="col-3 moderation-expanded-label" i18n>Updated</span>
48 <time class="col-9 moderation-expanded-text video-details-date-updated">{{ videoAbuse.updatedAt | date: 'medium' }}</time>
49 </div>
50
51 <!-- report text -->
52 <div class="mt-3 d-flex">
53 <span class="col-3 moderation-expanded-label">
54 <ng-container i18n>Report</ng-container>
55 <a [routerLink]="[ '/admin/moderation/video-abuses/list' ]" [queryParams]="{ 'search': '#' + videoAbuse.id }" class="ml-1 text-muted">#{{ videoAbuse.id }}</a>
56 </span>
57 <span class="col-9 moderation-expanded-text" [innerHTML]="videoAbuse.reasonHtml"></span>
58 </div>
59
60 <div *ngIf="getPredefinedReasons()" class="mt-2 d-flex">
61 <span class="col-3"></span>
62 <span class="col-9">
63 <a [routerLink]="[ '/admin/moderation/video-abuses/list' ]" [queryParams]="{ 'search': 'tag:' + reason.id }" class="chip rectangular bg-secondary text-light" *ngFor="let reason of getPredefinedReasons()">
64 <div>{{ reason.label }}</div>
65 </a>
66 </span>
67 </div>
68
69 <div *ngIf="videoAbuse.startAt" class="mt-2 d-flex">
70 <span class="col-3 moderation-expanded-label" i18n>Reported part</span>
71 <span class="col-9">
72 {{ startAt }}<ng-container *ngIf="videoAbuse.endAt"> - {{ endAt }}</ng-container>
73 </span>
74 </div>
75
76 <div class="mt-3 d-flex" *ngIf="videoAbuse.moderationComment">
77 <span class="col-3 moderation-expanded-label" i18n>Note</span>
78 <span class="col-9 moderation-expanded-text" [innerHTML]="videoAbuse.moderationCommentHtml"></span>
79 </div>
80
81 </div>
82
83 <!-- report right part (video details) -->
84 <div class="col-4">
85 <div class="screenratio">
86 <div *ngIf="videoAbuse.video.deleted || videoAbuse.video.blacklisted">
87 <span i18n *ngIf="videoAbuse.video.deleted">The video was deleted</span>
88 <span i18n *ngIf="!videoAbuse.video.deleted">The video was blocked</span>
89 </div>
90 <div *ngIf="!videoAbuse.video.deleted && !videoAbuse.video.blacklisted" [innerHTML]="videoAbuse.embedHtml"></div>
91 </div>
92 </div>
93</div>