diff options
author | Chocobozzz <me@florianbigard.com> | 2020-07-09 11:58:46 +0200 |
---|---|---|
committer | Chocobozzz <chocobozzz@cpy.re> | 2020-07-10 14:02:41 +0200 |
commit | 8ca56654a176ee8f350d31282c6cac4a59f58499 (patch) | |
tree | 6e52ed0d8410abfceb62bcb6230b8ed50bd6c574 /client/src/app/shared/shared-moderation | |
parent | 310b5219b38427f0c2c7ba57225afdd8f3064380 (diff) | |
download | PeerTube-8ca56654a176ee8f350d31282c6cac4a59f58499.tar.gz PeerTube-8ca56654a176ee8f350d31282c6cac4a59f58499.tar.zst PeerTube-8ca56654a176ee8f350d31282c6cac4a59f58499.zip |
Add ability to report comments in front end
Diffstat (limited to 'client/src/app/shared/shared-moderation')
8 files changed, 260 insertions, 66 deletions
diff --git a/client/src/app/shared/shared-moderation/abuse.service.ts b/client/src/app/shared/shared-moderation/abuse.service.ts index f45018d5c..95ac16955 100644 --- a/client/src/app/shared/shared-moderation/abuse.service.ts +++ b/client/src/app/shared/shared-moderation/abuse.service.ts | |||
@@ -5,18 +5,20 @@ import { catchError, map } from 'rxjs/operators' | |||
5 | import { HttpClient, HttpParams } from '@angular/common/http' | 5 | import { HttpClient, HttpParams } from '@angular/common/http' |
6 | import { Injectable } from '@angular/core' | 6 | import { Injectable } from '@angular/core' |
7 | import { RestExtractor, RestPagination, RestService } from '@app/core' | 7 | import { RestExtractor, RestPagination, RestService } from '@app/core' |
8 | import { AbuseUpdate, ResultList, Abuse, AbuseCreate, AbuseState } from '@shared/models' | 8 | import { Abuse, AbuseCreate, AbuseFilter, AbusePredefinedReasonsString, AbuseState, AbuseUpdate, ResultList } from '@shared/models' |
9 | import { environment } from '../../../environments/environment' | 9 | import { environment } from '../../../environments/environment' |
10 | import { I18n } from '@ngx-translate/i18n-polyfill' | ||
10 | 11 | ||
11 | @Injectable() | 12 | @Injectable() |
12 | export class AbuseService { | 13 | export class AbuseService { |
13 | private static BASE_ABUSE_URL = environment.apiUrl + '/api/v1/abuses' | 14 | private static BASE_ABUSE_URL = environment.apiUrl + '/api/v1/abuses' |
14 | 15 | ||
15 | constructor ( | 16 | constructor ( |
17 | private i18n: I18n, | ||
16 | private authHttp: HttpClient, | 18 | private authHttp: HttpClient, |
17 | private restService: RestService, | 19 | private restService: RestService, |
18 | private restExtractor: RestExtractor | 20 | private restExtractor: RestExtractor |
19 | ) {} | 21 | ) { } |
20 | 22 | ||
21 | getAbuses (options: { | 23 | getAbuses (options: { |
22 | pagination: RestPagination, | 24 | pagination: RestPagination, |
@@ -24,7 +26,7 @@ export class AbuseService { | |||
24 | search?: string | 26 | search?: string |
25 | }): Observable<ResultList<Abuse>> { | 27 | }): Observable<ResultList<Abuse>> { |
26 | const { pagination, sort, search } = options | 28 | const { pagination, sort, search } = options |
27 | const url = AbuseService.BASE_ABUSE_URL + 'abuse' | 29 | const url = AbuseService.BASE_ABUSE_URL |
28 | 30 | ||
29 | let params = new HttpParams() | 31 | let params = new HttpParams() |
30 | params = this.restService.addRestGetParams(params, pagination, sort) | 32 | params = this.restService.addRestGetParams(params, pagination, sort) |
@@ -60,39 +62,93 @@ export class AbuseService { | |||
60 | } | 62 | } |
61 | 63 | ||
62 | return this.authHttp.get<ResultList<Abuse>>(url, { params }) | 64 | return this.authHttp.get<ResultList<Abuse>>(url, { params }) |
63 | .pipe( | 65 | .pipe( |
64 | catchError(res => this.restExtractor.handleError(res)) | 66 | catchError(res => this.restExtractor.handleError(res)) |
65 | ) | 67 | ) |
66 | } | 68 | } |
67 | 69 | ||
68 | reportVideo (parameters: AbuseCreate) { | 70 | reportVideo (parameters: AbuseCreate) { |
69 | const url = AbuseService.BASE_ABUSE_URL | 71 | const url = AbuseService.BASE_ABUSE_URL |
70 | 72 | ||
71 | const body = omit(parameters, [ 'id' ]) | 73 | const body = omit(parameters, ['id']) |
72 | 74 | ||
73 | return this.authHttp.post(url, body) | 75 | return this.authHttp.post(url, body) |
74 | .pipe( | 76 | .pipe( |
75 | map(this.restExtractor.extractDataBool), | 77 | map(this.restExtractor.extractDataBool), |
76 | catchError(res => this.restExtractor.handleError(res)) | 78 | catchError(res => this.restExtractor.handleError(res)) |
77 | ) | 79 | ) |
78 | } | 80 | } |
79 | 81 | ||
80 | updateAbuse (abuse: Abuse, abuseUpdate: AbuseUpdate) { | 82 | updateAbuse (abuse: Abuse, abuseUpdate: AbuseUpdate) { |
81 | const url = AbuseService.BASE_ABUSE_URL + '/' + abuse.id | 83 | const url = AbuseService.BASE_ABUSE_URL + '/' + abuse.id |
82 | 84 | ||
83 | return this.authHttp.put(url, abuseUpdate) | 85 | return this.authHttp.put(url, abuseUpdate) |
84 | .pipe( | 86 | .pipe( |
85 | map(this.restExtractor.extractDataBool), | 87 | map(this.restExtractor.extractDataBool), |
86 | catchError(res => this.restExtractor.handleError(res)) | 88 | catchError(res => this.restExtractor.handleError(res)) |
87 | ) | 89 | ) |
88 | } | 90 | } |
89 | 91 | ||
90 | removeAbuse (abuse: Abuse) { | 92 | removeAbuse (abuse: Abuse) { |
91 | const url = AbuseService.BASE_ABUSE_URL + '/' + abuse.id | 93 | const url = AbuseService.BASE_ABUSE_URL + '/' + abuse.id |
92 | 94 | ||
93 | return this.authHttp.delete(url) | 95 | return this.authHttp.delete(url) |
94 | .pipe( | 96 | .pipe( |
95 | map(this.restExtractor.extractDataBool), | 97 | map(this.restExtractor.extractDataBool), |
96 | catchError(res => this.restExtractor.handleError(res)) | 98 | catchError(res => this.restExtractor.handleError(res)) |
97 | ) | 99 | ) |
98 | }} | 100 | } |
101 | |||
102 | getPrefefinedReasons (type: AbuseFilter) { | ||
103 | let reasons: { id: AbusePredefinedReasonsString, label: string, description?: string, help?: string }[] = [ | ||
104 | { | ||
105 | id: 'violentOrRepulsive', | ||
106 | label: this.i18n('Violent or repulsive'), | ||
107 | help: this.i18n('Contains offensive, violent, or coarse language or iconography.') | ||
108 | }, | ||
109 | { | ||
110 | id: 'hatefulOrAbusive', | ||
111 | label: this.i18n('Hateful or abusive'), | ||
112 | help: this.i18n('Contains abusive, racist or sexist language or iconography.') | ||
113 | }, | ||
114 | { | ||
115 | id: 'spamOrMisleading', | ||
116 | label: this.i18n('Spam, ad or false news'), | ||
117 | help: this.i18n('Contains marketing, spam, purposefully deceitful news, or otherwise misleading thumbnail/text/tags. Please provide reputable sources to report hoaxes.') | ||
118 | }, | ||
119 | { | ||
120 | id: 'privacy', | ||
121 | label: this.i18n('Privacy breach or doxxing'), | ||
122 | help: this.i18n('Contains personal information that could be used to track, identify, contact or impersonate someone (e.g. name, address, phone number, email, or credit card details).') | ||
123 | }, | ||
124 | { | ||
125 | id: 'rights', | ||
126 | label: this.i18n('Intellectual property violation'), | ||
127 | help: this.i18n('Infringes my intellectual property or copyright, wrt. the regional rules with which the server must comply.') | ||
128 | }, | ||
129 | { | ||
130 | id: 'serverRules', | ||
131 | label: this.i18n('Breaks server rules'), | ||
132 | description: this.i18n('Anything not included in the above that breaks the terms of service, code of conduct, or general rules in place on the server.') | ||
133 | } | ||
134 | ] | ||
135 | |||
136 | if (type === 'video') { | ||
137 | reasons = reasons.concat([ | ||
138 | { | ||
139 | id: 'thumbnails', | ||
140 | label: this.i18n('Thumbnails'), | ||
141 | help: this.i18n('The above can only be seen in thumbnails.') | ||
142 | }, | ||
143 | { | ||
144 | id: 'captions', | ||
145 | label: this.i18n('Captions'), | ||
146 | help: this.i18n('The above can only be seen in captions (please describe which).') | ||
147 | } | ||
148 | ]) | ||
149 | } | ||
150 | |||
151 | return reasons | ||
152 | } | ||
153 | |||
154 | } | ||
diff --git a/client/src/app/shared/shared-moderation/comment-report.component.html b/client/src/app/shared/shared-moderation/comment-report.component.html new file mode 100644 index 000000000..1105b3788 --- /dev/null +++ b/client/src/app/shared/shared-moderation/comment-report.component.html | |||
@@ -0,0 +1,62 @@ | |||
1 | <ng-template #modal> | ||
2 | <div class="modal-header"> | ||
3 | <h4 i18n class="modal-title">Report comment</h4> | ||
4 | <my-global-icon iconName="cross" aria-label="Close" role="button" (click)="hide()"></my-global-icon> | ||
5 | </div> | ||
6 | |||
7 | <div class="modal-body"> | ||
8 | <form novalidate [formGroup]="form" (ngSubmit)="report()"> | ||
9 | |||
10 | <div class="row"> | ||
11 | <div class="col-5 form-group"> | ||
12 | |||
13 | <label i18n for="reportPredefinedReasons">What is the issue?</label> | ||
14 | |||
15 | <div class="ml-2 mt-2 d-flex flex-column"> | ||
16 | <ng-container formGroupName="predefinedReasons"> | ||
17 | |||
18 | <div class="form-group" *ngFor="let reason of predefinedReasons"> | ||
19 | <my-peertube-checkbox [inputName]="reason.id" [formControlName]="reason.id" [labelText]="reason.label"> | ||
20 | <ng-template *ngIf="reason.help" ptTemplate="help"> | ||
21 | <div [innerHTML]="reason.help"></div> | ||
22 | </ng-template> | ||
23 | |||
24 | <ng-container *ngIf="reason.description" ngProjectAs="description"> | ||
25 | <div [innerHTML]="reason.description"></div> | ||
26 | </ng-container> | ||
27 | </my-peertube-checkbox> | ||
28 | </div> | ||
29 | |||
30 | </ng-container> | ||
31 | </div> | ||
32 | |||
33 | </div> | ||
34 | |||
35 | <div class="col-7"> | ||
36 | <div i18n class="information"> | ||
37 | Your report will be sent to moderators of {{ currentHost }}<ng-container *ngIf="isRemoteComment()"> and will be forwarded to the comment origin ({{ originHost }}) too</ng-container>. | ||
38 | </div> | ||
39 | |||
40 | <div class="form-group"> | ||
41 | <textarea | ||
42 | i18n-placeholder placeholder="Please describe the issue..." formControlName="reason" ngbAutofocus | ||
43 | [ngClass]="{ 'input-error': formErrors['reason'] }" class="form-control" | ||
44 | ></textarea> | ||
45 | <div *ngIf="formErrors.reason" class="form-error"> | ||
46 | {{ formErrors.reason }} | ||
47 | </div> | ||
48 | </div> | ||
49 | </div> | ||
50 | </div> | ||
51 | |||
52 | <div class="form-group inputs"> | ||
53 | <input | ||
54 | type="button" role="button" i18n-value value="Cancel" class="action-button action-button-cancel" | ||
55 | (click)="hide()" (key.enter)="hide()" | ||
56 | > | ||
57 | <input type="submit" i18n-value value="Submit" class="action-button-submit" [disabled]="!form.valid"> | ||
58 | </div> | ||
59 | |||
60 | </form> | ||
61 | </div> | ||
62 | </ng-template> | ||
diff --git a/client/src/app/shared/shared-moderation/comment-report.component.scss b/client/src/app/shared/shared-moderation/comment-report.component.scss new file mode 100644 index 000000000..17a33d3a2 --- /dev/null +++ b/client/src/app/shared/shared-moderation/comment-report.component.scss | |||
@@ -0,0 +1,11 @@ | |||
1 | @import 'variables'; | ||
2 | @import 'mixins'; | ||
3 | |||
4 | .information { | ||
5 | margin-bottom: 20px; | ||
6 | } | ||
7 | |||
8 | textarea { | ||
9 | @include peertube-textarea(100%, 100px); | ||
10 | } | ||
11 | |||
diff --git a/client/src/app/shared/shared-moderation/comment-report.component.ts b/client/src/app/shared/shared-moderation/comment-report.component.ts new file mode 100644 index 000000000..5db4b2dc1 --- /dev/null +++ b/client/src/app/shared/shared-moderation/comment-report.component.ts | |||
@@ -0,0 +1,93 @@ | |||
1 | import { mapValues, pickBy } from 'lodash-es' | ||
2 | import { Component, Input, OnInit, ViewChild } from '@angular/core' | ||
3 | import { SafeHtml } from '@angular/platform-browser' | ||
4 | import { VideoComment } from '@app/+videos/+video-watch/comment/video-comment.model' | ||
5 | import { Notifier } from '@app/core' | ||
6 | import { AbuseValidatorsService, FormReactive, FormValidatorService } from '@app/shared/shared-forms' | ||
7 | import { NgbModal } from '@ng-bootstrap/ng-bootstrap' | ||
8 | import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap/modal/modal-ref' | ||
9 | import { I18n } from '@ngx-translate/i18n-polyfill' | ||
10 | import { abusePredefinedReasonsMap, AbusePredefinedReasonsString } from '@shared/models' | ||
11 | import { AbuseService } from './abuse.service' | ||
12 | |||
13 | @Component({ | ||
14 | selector: 'my-comment-report', | ||
15 | templateUrl: './comment-report.component.html', | ||
16 | styleUrls: [ './comment-report.component.scss' ] | ||
17 | }) | ||
18 | export class CommentReportComponent extends FormReactive implements OnInit { | ||
19 | @Input() comment: VideoComment = null | ||
20 | |||
21 | @ViewChild('modal', { static: true }) modal: NgbModal | ||
22 | |||
23 | error: string = null | ||
24 | predefinedReasons: { id: AbusePredefinedReasonsString, label: string, description?: string, help?: string }[] = [] | ||
25 | embedHtml: SafeHtml | ||
26 | |||
27 | private openedModal: NgbModalRef | ||
28 | |||
29 | constructor ( | ||
30 | protected formValidatorService: FormValidatorService, | ||
31 | private modalService: NgbModal, | ||
32 | private abuseValidatorsService: AbuseValidatorsService, | ||
33 | private abuseService: AbuseService, | ||
34 | private notifier: Notifier, | ||
35 | private i18n: I18n | ||
36 | ) { | ||
37 | super() | ||
38 | } | ||
39 | |||
40 | get currentHost () { | ||
41 | return window.location.host | ||
42 | } | ||
43 | |||
44 | get originHost () { | ||
45 | if (this.isRemoteComment()) { | ||
46 | return this.comment.account.host | ||
47 | } | ||
48 | |||
49 | return '' | ||
50 | } | ||
51 | |||
52 | ngOnInit () { | ||
53 | this.buildForm({ | ||
54 | reason: this.abuseValidatorsService.ABUSE_REASON, | ||
55 | predefinedReasons: mapValues(abusePredefinedReasonsMap, r => null) | ||
56 | }) | ||
57 | |||
58 | this.predefinedReasons = this.abuseService.getPrefefinedReasons('comment') | ||
59 | } | ||
60 | |||
61 | show () { | ||
62 | this.openedModal = this.modalService.open(this.modal, { centered: true, keyboard: false, size: 'lg' }) | ||
63 | } | ||
64 | |||
65 | hide () { | ||
66 | this.openedModal.close() | ||
67 | this.openedModal = null | ||
68 | } | ||
69 | |||
70 | report () { | ||
71 | const reason = this.form.get('reason').value | ||
72 | const predefinedReasons = Object.keys(pickBy(this.form.get('predefinedReasons').value)) as AbusePredefinedReasonsString[] | ||
73 | |||
74 | this.abuseService.reportVideo({ | ||
75 | reason, | ||
76 | predefinedReasons, | ||
77 | comment: { | ||
78 | id: this.comment.id | ||
79 | } | ||
80 | }).subscribe( | ||
81 | () => { | ||
82 | this.notifier.success(this.i18n('Comment reported.')) | ||
83 | this.hide() | ||
84 | }, | ||
85 | |||
86 | err => this.notifier.error(err.message) | ||
87 | ) | ||
88 | } | ||
89 | |||
90 | isRemoteComment () { | ||
91 | return !this.comment.isLocal | ||
92 | } | ||
93 | } | ||
diff --git a/client/src/app/shared/shared-moderation/shared-moderation.module.ts b/client/src/app/shared/shared-moderation/shared-moderation.module.ts index 742193e58..ff4021a33 100644 --- a/client/src/app/shared/shared-moderation/shared-moderation.module.ts +++ b/client/src/app/shared/shared-moderation/shared-moderation.module.ts | |||
@@ -12,6 +12,7 @@ import { AbuseService } from './abuse.service' | |||
12 | import { VideoBlockComponent } from './video-block.component' | 12 | import { VideoBlockComponent } from './video-block.component' |
13 | import { VideoBlockService } from './video-block.service' | 13 | import { VideoBlockService } from './video-block.service' |
14 | import { VideoReportComponent } from './video-report.component' | 14 | import { VideoReportComponent } from './video-report.component' |
15 | import { CommentReportComponent } from './comment-report.component' | ||
15 | 16 | ||
16 | @NgModule({ | 17 | @NgModule({ |
17 | imports: [ | 18 | imports: [ |
@@ -25,7 +26,8 @@ import { VideoReportComponent } from './video-report.component' | |||
25 | UserModerationDropdownComponent, | 26 | UserModerationDropdownComponent, |
26 | VideoBlockComponent, | 27 | VideoBlockComponent, |
27 | VideoReportComponent, | 28 | VideoReportComponent, |
28 | BatchDomainsModalComponent | 29 | BatchDomainsModalComponent, |
30 | CommentReportComponent | ||
29 | ], | 31 | ], |
30 | 32 | ||
31 | exports: [ | 33 | exports: [ |
@@ -33,7 +35,8 @@ import { VideoReportComponent } from './video-report.component' | |||
33 | UserModerationDropdownComponent, | 35 | UserModerationDropdownComponent, |
34 | VideoBlockComponent, | 36 | VideoBlockComponent, |
35 | VideoReportComponent, | 37 | VideoReportComponent, |
36 | BatchDomainsModalComponent | 38 | BatchDomainsModalComponent, |
39 | CommentReportComponent | ||
37 | ], | 40 | ], |
38 | 41 | ||
39 | providers: [ | 42 | providers: [ |
diff --git a/client/src/app/shared/shared-moderation/user-moderation-dropdown.component.ts b/client/src/app/shared/shared-moderation/user-moderation-dropdown.component.ts index d3c37f082..78c2658df 100644 --- a/client/src/app/shared/shared-moderation/user-moderation-dropdown.component.ts +++ b/client/src/app/shared/shared-moderation/user-moderation-dropdown.component.ts | |||
@@ -16,6 +16,7 @@ export class UserModerationDropdownComponent implements OnInit, OnChanges { | |||
16 | 16 | ||
17 | @Input() user: User | 17 | @Input() user: User |
18 | @Input() account: Account | 18 | @Input() account: Account |
19 | @Input() prependActions: DropdownAction<{ user: User, account: Account }>[] | ||
19 | 20 | ||
20 | @Input() buttonSize: 'normal' | 'small' = 'normal' | 21 | @Input() buttonSize: 'normal' | 'small' = 'normal' |
21 | @Input() placement = 'left-top left-bottom auto' | 22 | @Input() placement = 'left-top left-bottom auto' |
@@ -250,6 +251,12 @@ export class UserModerationDropdownComponent implements OnInit, OnChanges { | |||
250 | private buildActions () { | 251 | private buildActions () { |
251 | this.userActions = [] | 252 | this.userActions = [] |
252 | 253 | ||
254 | if (this.prependActions) { | ||
255 | this.userActions = [ | ||
256 | this.prependActions | ||
257 | ] | ||
258 | } | ||
259 | |||
253 | if (this.authService.isLoggedIn()) { | 260 | if (this.authService.isLoggedIn()) { |
254 | const authUser = this.authService.getUser() | 261 | const authUser = this.authService.getUser() |
255 | 262 | ||
diff --git a/client/src/app/shared/shared-moderation/video-report.component.html b/client/src/app/shared/shared-moderation/video-report.component.html index d6beb6d2a..b724ecb18 100644 --- a/client/src/app/shared/shared-moderation/video-report.component.html +++ b/client/src/app/shared/shared-moderation/video-report.component.html | |||
@@ -14,16 +14,19 @@ | |||
14 | 14 | ||
15 | <div class="ml-2 mt-2 d-flex flex-column"> | 15 | <div class="ml-2 mt-2 d-flex flex-column"> |
16 | <ng-container formGroupName="predefinedReasons"> | 16 | <ng-container formGroupName="predefinedReasons"> |
17 | |||
17 | <div class="form-group" *ngFor="let reason of predefinedReasons"> | 18 | <div class="form-group" *ngFor="let reason of predefinedReasons"> |
18 | <my-peertube-checkbox formControlName="{{reason.id}}" labelText="{{reason.label}}"> | 19 | <my-peertube-checkbox [inputName]="reason.id" [formControlName]="reason.id" [labelText]="reason.label"> |
19 | <ng-template *ngIf="reason.help" ptTemplate="help"> | 20 | <ng-template *ngIf="reason.help" ptTemplate="help"> |
20 | <div [innerHTML]="reason.help"></div> | 21 | <div [innerHTML]="reason.help"></div> |
21 | </ng-template> | 22 | </ng-template> |
23 | |||
22 | <ng-container *ngIf="reason.description" ngProjectAs="description"> | 24 | <ng-container *ngIf="reason.description" ngProjectAs="description"> |
23 | <div [innerHTML]="reason.description"></div> | 25 | <div [innerHTML]="reason.description"></div> |
24 | </ng-container> | 26 | </ng-container> |
25 | </my-peertube-checkbox> | 27 | </my-peertube-checkbox> |
26 | </div> | 28 | </div> |
29 | |||
27 | </ng-container> | 30 | </ng-container> |
28 | </div> | 31 | </div> |
29 | 32 | ||
@@ -73,7 +76,7 @@ | |||
73 | </div> | 76 | </div> |
74 | 77 | ||
75 | <div class="form-group"> | 78 | <div class="form-group"> |
76 | <textarea | 79 | <textarea |
77 | i18n-placeholder placeholder="Please describe the issue..." formControlName="reason" ngbAutofocus | 80 | i18n-placeholder placeholder="Please describe the issue..." formControlName="reason" ngbAutofocus |
78 | [ngClass]="{ 'input-error': formErrors['reason'] }" class="form-control" | 81 | [ngClass]="{ 'input-error': formErrors['reason'] }" class="form-control" |
79 | ></textarea> | 82 | ></textarea> |
diff --git a/client/src/app/shared/shared-moderation/video-report.component.ts b/client/src/app/shared/shared-moderation/video-report.component.ts index 7977e4cca..26e7b62ba 100644 --- a/client/src/app/shared/shared-moderation/video-report.component.ts +++ b/client/src/app/shared/shared-moderation/video-report.component.ts | |||
@@ -79,48 +79,7 @@ export class VideoReportComponent extends FormReactive implements OnInit { | |||
79 | } | 79 | } |
80 | }) | 80 | }) |
81 | 81 | ||
82 | this.predefinedReasons = [ | 82 | this.predefinedReasons = this.abuseService.getPrefefinedReasons('video') |
83 | { | ||
84 | id: 'violentOrRepulsive', | ||
85 | label: this.i18n('Violent or repulsive'), | ||
86 | help: this.i18n('Contains offensive, violent, or coarse language or iconography.') | ||
87 | }, | ||
88 | { | ||
89 | id: 'hatefulOrAbusive', | ||
90 | label: this.i18n('Hateful or abusive'), | ||
91 | help: this.i18n('Contains abusive, racist or sexist language or iconography.') | ||
92 | }, | ||
93 | { | ||
94 | id: 'spamOrMisleading', | ||
95 | label: this.i18n('Spam, ad or false news'), | ||
96 | help: this.i18n('Contains marketing, spam, purposefully deceitful news, or otherwise misleading thumbnail/text/tags. Please provide reputable sources to report hoaxes.') | ||
97 | }, | ||
98 | { | ||
99 | id: 'privacy', | ||
100 | label: this.i18n('Privacy breach or doxxing'), | ||
101 | help: this.i18n('Contains personal information that could be used to track, identify, contact or impersonate someone (e.g. name, address, phone number, email, or credit card details).') | ||
102 | }, | ||
103 | { | ||
104 | id: 'rights', | ||
105 | label: this.i18n('Intellectual property violation'), | ||
106 | help: this.i18n('Infringes my intellectual property or copyright, wrt. the regional rules with which the server must comply.') | ||
107 | }, | ||
108 | { | ||
109 | id: 'serverRules', | ||
110 | label: this.i18n('Breaks server rules'), | ||
111 | description: this.i18n('Anything not included in the above that breaks the terms of service, code of conduct, or general rules in place on the server.') | ||
112 | }, | ||
113 | { | ||
114 | id: 'thumbnails', | ||
115 | label: this.i18n('Thumbnails'), | ||
116 | help: this.i18n('The above can only be seen in thumbnails.') | ||
117 | }, | ||
118 | { | ||
119 | id: 'captions', | ||
120 | label: this.i18n('Captions'), | ||
121 | help: this.i18n('The above can only be seen in captions (please describe which).') | ||
122 | } | ||
123 | ] | ||
124 | 83 | ||
125 | this.embedHtml = this.getVideoEmbed() | 84 | this.embedHtml = this.getVideoEmbed() |
126 | } | 85 | } |