diff options
Diffstat (limited to 'client/src/app/shared')
6 files changed, 79 insertions, 53 deletions
diff --git a/client/src/app/shared/shared-main/account/actor.model.ts b/client/src/app/shared/shared-main/account/actor.model.ts index a54f51aa4..bd693860d 100644 --- a/client/src/app/shared/shared-main/account/actor.model.ts +++ b/client/src/app/shared/shared-main/account/actor.model.ts | |||
@@ -1,4 +1,4 @@ | |||
1 | import { getAbsoluteAPIUrl } from '@app/helpers' | 1 | import { getAbsoluteAPIUrl, getAPIHost } from '@app/helpers' |
2 | import { Actor as ServerActor, ActorImage } from '@shared/models' | 2 | import { Actor as ServerActor, ActorImage } from '@shared/models' |
3 | 3 | ||
4 | export abstract class Actor implements ServerActor { | 4 | export abstract class Actor implements ServerActor { |
@@ -32,8 +32,7 @@ export abstract class Actor implements ServerActor { | |||
32 | } | 32 | } |
33 | 33 | ||
34 | static CREATE_BY_STRING (accountName: string, host: string, forceHostname = false) { | 34 | static CREATE_BY_STRING (accountName: string, host: string, forceHostname = false) { |
35 | const absoluteAPIUrl = getAbsoluteAPIUrl() | 35 | const thisHost = getAPIHost() |
36 | const thisHost = new URL(absoluteAPIUrl).host | ||
37 | 36 | ||
38 | if (host.trim() === thisHost && !forceHostname) return accountName | 37 | if (host.trim() === thisHost && !forceHostname) return accountName |
39 | 38 | ||
@@ -41,8 +40,7 @@ export abstract class Actor implements ServerActor { | |||
41 | } | 40 | } |
42 | 41 | ||
43 | static IS_LOCAL (host: string) { | 42 | static IS_LOCAL (host: string) { |
44 | const absoluteAPIUrl = getAbsoluteAPIUrl() | 43 | const thisHost = getAPIHost() |
45 | const thisHost = new URL(absoluteAPIUrl).host | ||
46 | 44 | ||
47 | return host.trim() === thisHost | 45 | return host.trim() === thisHost |
48 | } | 46 | } |
diff --git a/client/src/app/shared/shared-moderation/blocklist.service.ts b/client/src/app/shared/shared-moderation/blocklist.service.ts index f4836c6c4..3e92c2831 100644 --- a/client/src/app/shared/shared-moderation/blocklist.service.ts +++ b/client/src/app/shared/shared-moderation/blocklist.service.ts | |||
@@ -1,5 +1,6 @@ | |||
1 | import { SortMeta } from 'primeng/api' | 1 | import { SortMeta } from 'primeng/api' |
2 | import { catchError, map } from 'rxjs/operators' | 2 | import { from } from 'rxjs' |
3 | import { catchError, concatMap, map, toArray } from 'rxjs/operators' | ||
3 | import { HttpClient, HttpParams } from '@angular/common/http' | 4 | import { HttpClient, HttpParams } from '@angular/common/http' |
4 | import { Injectable } from '@angular/core' | 5 | import { Injectable } from '@angular/core' |
5 | import { RestExtractor, RestPagination, RestService } from '@app/core' | 6 | import { RestExtractor, RestPagination, RestService } from '@app/core' |
@@ -120,11 +121,15 @@ export class BlocklistService { | |||
120 | ) | 121 | ) |
121 | } | 122 | } |
122 | 123 | ||
123 | blockAccountByInstance (account: Pick<Account, 'nameWithHost'>) { | 124 | blockAccountByInstance (accountsArg: Pick<Account, 'nameWithHost'> | Pick<Account, 'nameWithHost'>[]) { |
124 | const body = { accountName: account.nameWithHost } | 125 | const accounts = Array.isArray(accountsArg) ? accountsArg : [ accountsArg ] |
125 | 126 | ||
126 | return this.authHttp.post(BlocklistService.BASE_SERVER_BLOCKLIST_URL + '/accounts', body) | 127 | return from(accounts) |
127 | .pipe(catchError(err => this.restExtractor.handleError(err))) | 128 | .pipe( |
129 | concatMap(a => this.authHttp.post(BlocklistService.BASE_SERVER_BLOCKLIST_URL + '/accounts', { accountName: a.nameWithHost })), | ||
130 | toArray(), | ||
131 | catchError(err => this.restExtractor.handleError(err)) | ||
132 | ) | ||
128 | } | 133 | } |
129 | 134 | ||
130 | unblockAccountByInstance (account: Pick<Account, 'nameWithHost'>) { | 135 | unblockAccountByInstance (account: Pick<Account, 'nameWithHost'>) { |
diff --git a/client/src/app/shared/shared-moderation/user-ban-modal.component.html b/client/src/app/shared/shared-moderation/user-ban-modal.component.html index 6c83cc9cc..2b6726bdc 100644 --- a/client/src/app/shared/shared-moderation/user-ban-modal.component.html +++ b/client/src/app/shared/shared-moderation/user-ban-modal.component.html | |||
@@ -1,11 +1,15 @@ | |||
1 | <ng-template #modal> | 1 | <ng-template #modal> |
2 | <div class="modal-header"> | 2 | <div class="modal-header"> |
3 | <h4 i18n class="modal-title">Ban</h4> | 3 | <h4 i18n class="modal-title">{{ getModalTitle() }}</h4> |
4 | 4 | ||
5 | <my-global-icon iconName="cross" aria-label="Close" role="button" (click)="hide()"></my-global-icon> | 5 | <my-global-icon iconName="cross" aria-label="Close" role="button" (click)="hide()"></my-global-icon> |
6 | </div> | 6 | </div> |
7 | 7 | ||
8 | <div class="modal-body"> | 8 | <div class="modal-body"> |
9 | <div class="description" i18n> | ||
10 | A banned user will no longer be able to login. | ||
11 | </div> | ||
12 | |||
9 | <form novalidate [formGroup]="form" (ngSubmit)="banUser()"> | 13 | <form novalidate [formGroup]="form" (ngSubmit)="banUser()"> |
10 | <div class="form-group"> | 14 | <div class="form-group"> |
11 | <textarea | 15 | <textarea |
@@ -17,15 +21,12 @@ | |||
17 | </div> | 21 | </div> |
18 | </div> | 22 | </div> |
19 | 23 | ||
20 | <div i18n> | ||
21 | A banned user will no longer be able to login. | ||
22 | </div> | ||
23 | |||
24 | <div class="form-group"> | 24 | <div class="form-group"> |
25 | <my-peertube-checkbox | 25 | <my-peertube-checkbox |
26 | inputName="banMute" formControlName="mute" | 26 | inputName="banMute" formControlName="mute" |
27 | i18n-labelText labelText="Mute this account" | 27 | i18n-labelText labelText="Mute to also hide videos/comments" |
28 | ></my-peertube-checkbox> | 28 | > |
29 | </my-peertube-checkbox> | ||
29 | </div> | 30 | </div> |
30 | 31 | ||
31 | <div class="form-group inputs"> | 32 | <div class="form-group inputs"> |
@@ -34,7 +35,7 @@ | |||
34 | (click)="hide()" (key.enter)="hide()" | 35 | (click)="hide()" (key.enter)="hide()" |
35 | > | 36 | > |
36 | 37 | ||
37 | <input type="submit" i18n-value [value]="modalMessage" class="peertube-button orange-button" [disabled]="!form.valid" /> | 38 | <input type="submit" i18n-value [value]="getModalTitle()" class="peertube-button orange-button" [disabled]="!form.valid" /> |
38 | </div> | 39 | </div> |
39 | </form> | 40 | </form> |
40 | </div> | 41 | </div> |
diff --git a/client/src/app/shared/shared-moderation/user-ban-modal.component.scss b/client/src/app/shared/shared-moderation/user-ban-modal.component.scss index 08e072d8f..2c46c3d03 100644 --- a/client/src/app/shared/shared-moderation/user-ban-modal.component.scss +++ b/client/src/app/shared/shared-moderation/user-ban-modal.component.scss | |||
@@ -1,6 +1,11 @@ | |||
1 | @use '_variables' as *; | 1 | @use '_variables' as *; |
2 | @use '_mixins' as *; | 2 | @use '_mixins' as *; |
3 | 3 | ||
4 | .description { | ||
5 | font-size: 15px; | ||
6 | margin-bottom: 15px; | ||
7 | } | ||
8 | |||
4 | textarea { | 9 | textarea { |
5 | @include peertube-textarea(100%, 60px); | 10 | @include peertube-textarea(100%, 60px); |
6 | } | 11 | } |
diff --git a/client/src/app/shared/shared-moderation/user-ban-modal.component.ts b/client/src/app/shared/shared-moderation/user-ban-modal.component.ts index b3f03990f..9edfac388 100644 --- a/client/src/app/shared/shared-moderation/user-ban-modal.component.ts +++ b/client/src/app/shared/shared-moderation/user-ban-modal.component.ts | |||
@@ -1,3 +1,4 @@ | |||
1 | import { forkJoin } from 'rxjs' | ||
1 | import { Component, EventEmitter, OnInit, Output, ViewChild } from '@angular/core' | 2 | import { Component, EventEmitter, OnInit, Output, ViewChild } from '@angular/core' |
2 | import { Notifier } from '@app/core' | 3 | import { Notifier } from '@app/core' |
3 | import { FormReactive, FormValidatorService } from '@app/shared/shared-forms' | 4 | import { FormReactive, FormValidatorService } from '@app/shared/shared-forms' |
@@ -42,9 +43,6 @@ export class UserBanModalComponent extends FormReactive implements OnInit { | |||
42 | openModal (user: User | User[]) { | 43 | openModal (user: User | User[]) { |
43 | this.usersToBan = user | 44 | this.usersToBan = user |
44 | this.openedModal = this.modalService.open(this.modal, { centered: true }) | 45 | this.openedModal = this.modalService.open(this.modal, { centered: true }) |
45 | |||
46 | const isSingleUser = !(Array.isArray(this.usersToBan) && this.usersToBan.length > 1) | ||
47 | this.modalMessage = isSingleUser ? $localize`Ban this user` : $localize`Ban these users` | ||
48 | } | 46 | } |
49 | 47 | ||
50 | hide () { | 48 | hide () { |
@@ -56,7 +54,13 @@ export class UserBanModalComponent extends FormReactive implements OnInit { | |||
56 | const reason = this.form.value['reason'] || undefined | 54 | const reason = this.form.value['reason'] || undefined |
57 | const mute = this.form.value['mute'] | 55 | const mute = this.form.value['mute'] |
58 | 56 | ||
59 | this.userAdminService.banUsers(this.usersToBan, reason) | 57 | const observables = [ |
58 | this.userAdminService.banUsers(this.usersToBan, reason) | ||
59 | ] | ||
60 | |||
61 | if (mute) observables.push(this.muteAccounts()) | ||
62 | |||
63 | forkJoin(observables) | ||
60 | .subscribe({ | 64 | .subscribe({ |
61 | next: () => { | 65 | next: () => { |
62 | const message = Array.isArray(this.usersToBan) | 66 | const message = Array.isArray(this.usersToBan) |
@@ -67,22 +71,6 @@ export class UserBanModalComponent extends FormReactive implements OnInit { | |||
67 | 71 | ||
68 | this.userBanned.emit(this.usersToBan) | 72 | this.userBanned.emit(this.usersToBan) |
69 | 73 | ||
70 | if (mute) { | ||
71 | const users = Array.isArray(this.usersToBan) ? this.usersToBan : [ this.usersToBan ] | ||
72 | users.forEach(user => { | ||
73 | const account = new Account(user.account) | ||
74 | this.blocklistService.blockAccountByInstance(account) | ||
75 | .subscribe({ | ||
76 | next: () => { | ||
77 | this.notifier.success($localize`Account ${user.username} muted by the instance.`) | ||
78 | account.mutedByInstance = true | ||
79 | }, | ||
80 | |||
81 | error: err => this.notifier.error(err.message) | ||
82 | }) | ||
83 | }) | ||
84 | } | ||
85 | |||
86 | this.hide() | 74 | this.hide() |
87 | }, | 75 | }, |
88 | 76 | ||
@@ -90,4 +78,17 @@ export class UserBanModalComponent extends FormReactive implements OnInit { | |||
90 | }) | 78 | }) |
91 | } | 79 | } |
92 | 80 | ||
81 | getModalTitle () { | ||
82 | if (Array.isArray(this.usersToBan)) return $localize`Ban ${this.usersToBan.length} users` | ||
83 | |||
84 | return $localize`Ban "${this.usersToBan.username}"` | ||
85 | } | ||
86 | |||
87 | private muteAccounts () { | ||
88 | const accounts = Array.isArray(this.usersToBan) | ||
89 | ? this.usersToBan.map(u => new Account(u.account)) | ||
90 | : new Account(this.usersToBan.account) | ||
91 | |||
92 | return this.blocklistService.blockAccountByInstance(accounts) | ||
93 | } | ||
93 | } | 94 | } |
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 0d19565ef..787318c2c 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 | |||
@@ -7,6 +7,16 @@ import { BlocklistService } from './blocklist.service' | |||
7 | import { BulkService } from './bulk.service' | 7 | import { BulkService } from './bulk.service' |
8 | import { UserBanModalComponent } from './user-ban-modal.component' | 8 | import { UserBanModalComponent } from './user-ban-modal.component' |
9 | 9 | ||
10 | export type AccountMutedStatus = | ||
11 | Pick<Account, 'id' | 'nameWithHost' | 'host' | 'userId' | | ||
12 | 'mutedByInstance' | 'mutedByUser' | 'mutedServerByInstance' | 'mutedServerByUser'> | ||
13 | |||
14 | export type UserModerationDisplayType = { | ||
15 | myAccount?: boolean | ||
16 | instanceAccount?: boolean | ||
17 | instanceUser?: boolean | ||
18 | } | ||
19 | |||
10 | @Component({ | 20 | @Component({ |
11 | selector: 'my-user-moderation-dropdown', | 21 | selector: 'my-user-moderation-dropdown', |
12 | templateUrl: './user-moderation-dropdown.component.html' | 22 | templateUrl: './user-moderation-dropdown.component.html' |
@@ -15,8 +25,8 @@ export class UserModerationDropdownComponent implements OnInit, OnChanges { | |||
15 | @ViewChild('userBanModal') userBanModal: UserBanModalComponent | 25 | @ViewChild('userBanModal') userBanModal: UserBanModalComponent |
16 | 26 | ||
17 | @Input() user: User | 27 | @Input() user: User |
18 | @Input() account: Account | 28 | @Input() account: AccountMutedStatus |
19 | @Input() prependActions: DropdownAction<{ user: User, account: Account }>[] | 29 | @Input() prependActions: DropdownAction<{ user: User, account: AccountMutedStatus }>[] |
20 | 30 | ||
21 | @Input() buttonSize: 'normal' | 'small' = 'normal' | 31 | @Input() buttonSize: 'normal' | 'small' = 'normal' |
22 | @Input() buttonStyled = true | 32 | @Input() buttonStyled = true |
@@ -24,10 +34,16 @@ export class UserModerationDropdownComponent implements OnInit, OnChanges { | |||
24 | @Input() label: string | 34 | @Input() label: string |
25 | @Input() container: 'body' | undefined = undefined | 35 | @Input() container: 'body' | undefined = undefined |
26 | 36 | ||
37 | @Input() displayOptions: UserModerationDisplayType = { | ||
38 | myAccount: true, | ||
39 | instanceAccount: true, | ||
40 | instanceUser: true | ||
41 | } | ||
42 | |||
27 | @Output() userChanged = new EventEmitter() | 43 | @Output() userChanged = new EventEmitter() |
28 | @Output() userDeleted = new EventEmitter() | 44 | @Output() userDeleted = new EventEmitter() |
29 | 45 | ||
30 | userActions: DropdownAction<{ user: User, account: Account }>[][] = [] | 46 | userActions: DropdownAction<{ user: User, account: AccountMutedStatus }>[][] = [] |
31 | 47 | ||
32 | requiresEmailVerification = false | 48 | requiresEmailVerification = false |
33 | 49 | ||
@@ -111,7 +127,7 @@ export class UserModerationDropdownComponent implements OnInit, OnChanges { | |||
111 | }) | 127 | }) |
112 | } | 128 | } |
113 | 129 | ||
114 | blockAccountByUser (account: Account) { | 130 | blockAccountByUser (account: AccountMutedStatus) { |
115 | this.blocklistService.blockAccountByUser(account) | 131 | this.blocklistService.blockAccountByUser(account) |
116 | .subscribe({ | 132 | .subscribe({ |
117 | next: () => { | 133 | next: () => { |
@@ -125,7 +141,7 @@ export class UserModerationDropdownComponent implements OnInit, OnChanges { | |||
125 | }) | 141 | }) |
126 | } | 142 | } |
127 | 143 | ||
128 | unblockAccountByUser (account: Account) { | 144 | unblockAccountByUser (account: AccountMutedStatus) { |
129 | this.blocklistService.unblockAccountByUser(account) | 145 | this.blocklistService.unblockAccountByUser(account) |
130 | .subscribe({ | 146 | .subscribe({ |
131 | next: () => { | 147 | next: () => { |
@@ -167,7 +183,7 @@ export class UserModerationDropdownComponent implements OnInit, OnChanges { | |||
167 | }) | 183 | }) |
168 | } | 184 | } |
169 | 185 | ||
170 | blockAccountByInstance (account: Account) { | 186 | blockAccountByInstance (account: AccountMutedStatus) { |
171 | this.blocklistService.blockAccountByInstance(account) | 187 | this.blocklistService.blockAccountByInstance(account) |
172 | .subscribe({ | 188 | .subscribe({ |
173 | next: () => { | 189 | next: () => { |
@@ -181,7 +197,7 @@ export class UserModerationDropdownComponent implements OnInit, OnChanges { | |||
181 | }) | 197 | }) |
182 | } | 198 | } |
183 | 199 | ||
184 | unblockAccountByInstance (account: Account) { | 200 | unblockAccountByInstance (account: AccountMutedStatus) { |
185 | this.blocklistService.unblockAccountByInstance(account) | 201 | this.blocklistService.unblockAccountByInstance(account) |
186 | .subscribe({ | 202 | .subscribe({ |
187 | next: () => { | 203 | next: () => { |
@@ -246,7 +262,7 @@ export class UserModerationDropdownComponent implements OnInit, OnChanges { | |||
246 | return user && this.authService.getUser().id === user.id | 262 | return user && this.authService.getUser().id === user.id |
247 | } | 263 | } |
248 | 264 | ||
249 | private isMyAccount (account: Account) { | 265 | private isMyAccount (account: AccountMutedStatus) { |
250 | return account && this.authService.getUser().account.id === account.id | 266 | return account && this.authService.getUser().account.id === account.id |
251 | } | 267 | } |
252 | 268 | ||
@@ -267,9 +283,9 @@ export class UserModerationDropdownComponent implements OnInit, OnChanges { | |||
267 | } | 283 | } |
268 | 284 | ||
269 | private buildMyAccountModerationActions () { | 285 | private buildMyAccountModerationActions () { |
270 | if (!this.account || !this.authService.isLoggedIn()) return [] | 286 | if (!this.account || !this.displayOptions.myAccount || !this.authService.isLoggedIn()) return [] |
271 | 287 | ||
272 | const myAccountActions: DropdownAction<{ user: User, account: Account }>[] = [ | 288 | const myAccountActions: DropdownAction<{ user: User, account: AccountMutedStatus }>[] = [ |
273 | { | 289 | { |
274 | label: $localize`My account moderation`, | 290 | label: $localize`My account moderation`, |
275 | class: [ 'red' ], | 291 | class: [ 'red' ], |
@@ -315,9 +331,9 @@ export class UserModerationDropdownComponent implements OnInit, OnChanges { | |||
315 | 331 | ||
316 | const authUser = this.authService.getUser() | 332 | const authUser = this.authService.getUser() |
317 | 333 | ||
318 | let instanceActions: DropdownAction<{ user: User, account: Account }>[] = [] | 334 | let instanceActions: DropdownAction<{ user: User, account: AccountMutedStatus }>[] = [] |
319 | 335 | ||
320 | if (this.user && authUser.hasRight(UserRight.MANAGE_USERS) && authUser.canManage(this.user)) { | 336 | if (this.user && this.displayOptions.instanceUser && authUser.hasRight(UserRight.MANAGE_USERS) && authUser.canManage(this.user)) { |
321 | instanceActions = instanceActions.concat([ | 337 | instanceActions = instanceActions.concat([ |
322 | { | 338 | { |
323 | label: $localize`Edit user`, | 339 | label: $localize`Edit user`, |
@@ -351,7 +367,7 @@ export class UserModerationDropdownComponent implements OnInit, OnChanges { | |||
351 | } | 367 | } |
352 | 368 | ||
353 | // Instance actions on account blocklists | 369 | // Instance actions on account blocklists |
354 | if (this.account && authUser.hasRight(UserRight.MANAGE_ACCOUNTS_BLOCKLIST)) { | 370 | if (this.account && this.displayOptions.instanceAccount && authUser.hasRight(UserRight.MANAGE_ACCOUNTS_BLOCKLIST)) { |
355 | instanceActions = instanceActions.concat([ | 371 | instanceActions = instanceActions.concat([ |
356 | { | 372 | { |
357 | label: $localize`Mute this account`, | 373 | label: $localize`Mute this account`, |
@@ -369,7 +385,7 @@ export class UserModerationDropdownComponent implements OnInit, OnChanges { | |||
369 | } | 385 | } |
370 | 386 | ||
371 | // Instance actions on server blocklists | 387 | // Instance actions on server blocklists |
372 | if (this.account && authUser.hasRight(UserRight.MANAGE_SERVERS_BLOCKLIST)) { | 388 | if (this.account && this.displayOptions.instanceAccount && authUser.hasRight(UserRight.MANAGE_SERVERS_BLOCKLIST)) { |
373 | instanceActions = instanceActions.concat([ | 389 | instanceActions = instanceActions.concat([ |
374 | { | 390 | { |
375 | label: $localize`Mute the instance`, | 391 | label: $localize`Mute the instance`, |
@@ -386,7 +402,7 @@ export class UserModerationDropdownComponent implements OnInit, OnChanges { | |||
386 | ]) | 402 | ]) |
387 | } | 403 | } |
388 | 404 | ||
389 | if (this.account && authUser.hasRight(UserRight.REMOVE_ANY_VIDEO_COMMENT)) { | 405 | if (this.account && this.displayOptions.instanceAccount && authUser.hasRight(UserRight.REMOVE_ANY_VIDEO_COMMENT)) { |
390 | instanceActions = instanceActions.concat([ | 406 | instanceActions = instanceActions.concat([ |
391 | { | 407 | { |
392 | label: $localize`Remove comments from your instance`, | 408 | label: $localize`Remove comments from your instance`, |