aboutsummaryrefslogtreecommitdiffhomepage
path: root/client/src/app/+admin/moderation
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/moderation
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/moderation')
-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
13 files changed, 223 insertions, 216 deletions
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>