diff options
author | Chocobozzz <me@florianbigard.com> | 2018-10-15 16:43:14 +0200 |
---|---|---|
committer | Chocobozzz <me@florianbigard.com> | 2018-10-16 16:41:36 +0200 |
commit | 65b21c961c69c4a63c7c0c34be3d6d034a1176c7 (patch) | |
tree | 35ec4a16c90a1de99b2865fbabb368b683ca6c87 | |
parent | b44164bb567fe7c9f65f1ac2908d44990a8ccc8e (diff) | |
download | PeerTube-65b21c961c69c4a63c7c0c34be3d6d034a1176c7.tar.gz PeerTube-65b21c961c69c4a63c7c0c34be3d6d034a1176c7.tar.zst PeerTube-65b21c961c69c4a63c7c0c34be3d6d034a1176c7.zip |
Add ability to mute a user/instance by server in client
21 files changed, 437 insertions, 41 deletions
diff --git a/client/src/app/+accounts/accounts.component.html b/client/src/app/+accounts/accounts.component.html index 60dbcdf1d..c1377c1ea 100644 --- a/client/src/app/+accounts/accounts.component.html +++ b/client/src/app/+accounts/accounts.component.html | |||
@@ -10,8 +10,10 @@ | |||
10 | <div class="actor-name">{{ account.nameWithHost }}</div> | 10 | <div class="actor-name">{{ account.nameWithHost }}</div> |
11 | 11 | ||
12 | <span *ngIf="user?.blocked" [ngbTooltip]="user.blockedReason" class="badge badge-danger" i18n>Banned</span> | 12 | <span *ngIf="user?.blocked" [ngbTooltip]="user.blockedReason" class="badge badge-danger" i18n>Banned</span> |
13 | <span *ngIf="account.muted" class="badge badge-danger" i18n>Muted</span> | 13 | <span *ngIf="account.mutedByUser" class="badge badge-danger" i18n>Muted</span> |
14 | <span *ngIf="account.mutedServer" class="badge badge-danger" i18n>Instance muted</span> | 14 | <span *ngIf="account.mutedServerByUser" class="badge badge-danger" i18n>Muted by your instance</span> |
15 | <span *ngIf="account.mutedByInstance" class="badge badge-danger" i18n>Instance muted</span> | ||
16 | <span *ngIf="account.mutedServerByInstance" class="badge badge-danger" i18n>Instance muted by your instance</span> | ||
15 | 17 | ||
16 | <my-user-moderation-dropdown | 18 | <my-user-moderation-dropdown |
17 | buttonSize="small" [account]="account" [user]="user" | 19 | buttonSize="small" [account]="account" [user]="user" |
diff --git a/client/src/app/+admin/admin.module.ts b/client/src/app/+admin/admin.module.ts index 8c6db98d9..c06ae1d60 100644 --- a/client/src/app/+admin/admin.module.ts +++ b/client/src/app/+admin/admin.module.ts | |||
@@ -15,6 +15,7 @@ import { ModerationCommentModalComponent, VideoAbuseListComponent, VideoBlacklis | |||
15 | import { ModerationComponent } from '@app/+admin/moderation/moderation.component' | 15 | import { ModerationComponent } from '@app/+admin/moderation/moderation.component' |
16 | import { RedundancyCheckboxComponent } from '@app/+admin/follows/shared/redundancy-checkbox.component' | 16 | import { RedundancyCheckboxComponent } from '@app/+admin/follows/shared/redundancy-checkbox.component' |
17 | import { RedundancyService } from '@app/+admin/follows/shared/redundancy.service' | 17 | import { RedundancyService } from '@app/+admin/follows/shared/redundancy.service' |
18 | import { InstanceAccountBlocklistComponent, InstanceServerBlocklistComponent } from '@app/+admin/moderation/instance-blocklist' | ||
18 | 19 | ||
19 | @NgModule({ | 20 | @NgModule({ |
20 | imports: [ | 21 | imports: [ |
@@ -41,6 +42,8 @@ import { RedundancyService } from '@app/+admin/follows/shared/redundancy.service | |||
41 | VideoBlacklistListComponent, | 42 | VideoBlacklistListComponent, |
42 | VideoAbuseListComponent, | 43 | VideoAbuseListComponent, |
43 | ModerationCommentModalComponent, | 44 | ModerationCommentModalComponent, |
45 | InstanceServerBlocklistComponent, | ||
46 | InstanceAccountBlocklistComponent, | ||
44 | 47 | ||
45 | JobsComponent, | 48 | JobsComponent, |
46 | JobsListComponent, | 49 | JobsListComponent, |
diff --git a/client/src/app/+admin/moderation/instance-blocklist/index.ts b/client/src/app/+admin/moderation/instance-blocklist/index.ts new file mode 100644 index 000000000..3e7a344bb --- /dev/null +++ b/client/src/app/+admin/moderation/instance-blocklist/index.ts | |||
@@ -0,0 +1,2 @@ | |||
1 | export * from './instance-account-blocklist.component' | ||
2 | export * from './instance-server-blocklist.component' | ||
diff --git a/client/src/app/+admin/moderation/instance-blocklist/instance-account-blocklist.component.html b/client/src/app/+admin/moderation/instance-blocklist/instance-account-blocklist.component.html new file mode 100644 index 000000000..7797bc56e --- /dev/null +++ b/client/src/app/+admin/moderation/instance-blocklist/instance-account-blocklist.component.html | |||
@@ -0,0 +1,22 @@ | |||
1 | <p-table | ||
2 | [value]="blockedAccounts" [lazy]="true" [paginator]="true" [totalRecords]="totalRecords" [rows]="rowsPerPage" | ||
3 | [sortField]="sort.field" [sortOrder]="sort.order" (onLazyLoad)="loadLazy($event)" | ||
4 | > | ||
5 | |||
6 | <ng-template pTemplate="header"> | ||
7 | <tr> | ||
8 | <th i18n>Account</th> | ||
9 | <th i18n pSortableColumn="createdAt">Muted at <p-sortIcon field="createdAt"></p-sortIcon></th> | ||
10 | </tr> | ||
11 | </ng-template> | ||
12 | |||
13 | <ng-template pTemplate="body" let-accountBlock> | ||
14 | <tr> | ||
15 | <td>{{ accountBlock.blockedAccount.nameWithHost }}</td> | ||
16 | <td>{{ accountBlock.createdAt }}</td> | ||
17 | <td class="action-cell"> | ||
18 | <button class="unblock-button" (click)="unblockAccount(accountBlock)" i18n>Unmute</button> | ||
19 | </td> | ||
20 | </tr> | ||
21 | </ng-template> | ||
22 | </p-table> | ||
diff --git a/client/src/app/+admin/moderation/instance-blocklist/instance-account-blocklist.component.scss b/client/src/app/+admin/moderation/instance-blocklist/instance-account-blocklist.component.scss new file mode 100644 index 000000000..6028b75ea --- /dev/null +++ b/client/src/app/+admin/moderation/instance-blocklist/instance-account-blocklist.component.scss | |||
@@ -0,0 +1,7 @@ | |||
1 | @import '_variables'; | ||
2 | @import '_mixins'; | ||
3 | |||
4 | .unblock-button { | ||
5 | @include peertube-button; | ||
6 | @include grey-button; | ||
7 | } \ No newline at end of file | ||
diff --git a/client/src/app/+admin/moderation/instance-blocklist/instance-account-blocklist.component.ts b/client/src/app/+admin/moderation/instance-blocklist/instance-account-blocklist.component.ts new file mode 100644 index 000000000..3f243aee4 --- /dev/null +++ b/client/src/app/+admin/moderation/instance-blocklist/instance-account-blocklist.component.ts | |||
@@ -0,0 +1,59 @@ | |||
1 | import { Component, OnInit } from '@angular/core' | ||
2 | import { NotificationsService } from 'angular2-notifications' | ||
3 | import { I18n } from '@ngx-translate/i18n-polyfill' | ||
4 | import { RestPagination, RestTable } from '@app/shared' | ||
5 | import { SortMeta } from 'primeng/components/common/sortmeta' | ||
6 | import { BlocklistService, AccountBlock } from '@app/shared/blocklist' | ||
7 | |||
8 | @Component({ | ||
9 | selector: 'my-instance-account-blocklist', | ||
10 | styleUrls: [ './instance-account-blocklist.component.scss' ], | ||
11 | templateUrl: './instance-account-blocklist.component.html' | ||
12 | }) | ||
13 | export class InstanceAccountBlocklistComponent extends RestTable implements OnInit { | ||
14 | blockedAccounts: AccountBlock[] = [] | ||
15 | totalRecords = 0 | ||
16 | rowsPerPage = 10 | ||
17 | sort: SortMeta = { field: 'createdAt', order: -1 } | ||
18 | pagination: RestPagination = { count: this.rowsPerPage, start: 0 } | ||
19 | |||
20 | constructor ( | ||
21 | private notificationsService: NotificationsService, | ||
22 | private blocklistService: BlocklistService, | ||
23 | private i18n: I18n | ||
24 | ) { | ||
25 | super() | ||
26 | } | ||
27 | |||
28 | ngOnInit () { | ||
29 | this.initialize() | ||
30 | } | ||
31 | |||
32 | unblockAccount (accountBlock: AccountBlock) { | ||
33 | const blockedAccount = accountBlock.blockedAccount | ||
34 | |||
35 | this.blocklistService.unblockAccountByInstance(blockedAccount) | ||
36 | .subscribe( | ||
37 | () => { | ||
38 | this.notificationsService.success( | ||
39 | this.i18n('Success'), | ||
40 | this.i18n('Account {{nameWithHost}} unmuted by your instance.', { nameWithHost: blockedAccount.nameWithHost }) | ||
41 | ) | ||
42 | |||
43 | this.loadData() | ||
44 | } | ||
45 | ) | ||
46 | } | ||
47 | |||
48 | protected loadData () { | ||
49 | return this.blocklistService.getInstanceAccountBlocklist(this.pagination, this.sort) | ||
50 | .subscribe( | ||
51 | resultList => { | ||
52 | this.blockedAccounts = resultList.data | ||
53 | this.totalRecords = resultList.total | ||
54 | }, | ||
55 | |||
56 | err => this.notificationsService.error(this.i18n('Error'), err.message) | ||
57 | ) | ||
58 | } | ||
59 | } | ||
diff --git a/client/src/app/+admin/moderation/instance-blocklist/instance-server-blocklist.component.html b/client/src/app/+admin/moderation/instance-blocklist/instance-server-blocklist.component.html new file mode 100644 index 000000000..859c0f916 --- /dev/null +++ b/client/src/app/+admin/moderation/instance-blocklist/instance-server-blocklist.component.html | |||
@@ -0,0 +1,23 @@ | |||
1 | <p-table | ||
2 | [value]="blockedAccounts" [lazy]="true" [paginator]="true" [totalRecords]="totalRecords" [rows]="rowsPerPage" | ||
3 | [sortField]="sort.field" [sortOrder]="sort.order" (onLazyLoad)="loadLazy($event)" | ||
4 | > | ||
5 | |||
6 | <ng-template pTemplate="header"> | ||
7 | <tr> | ||
8 | <th i18n>Instance</th> | ||
9 | <th i18n pSortableColumn="createdAt">Muted at <p-sortIcon field="createdAt"></p-sortIcon></th> | ||
10 | <th></th> | ||
11 | </tr> | ||
12 | </ng-template> | ||
13 | |||
14 | <ng-template pTemplate="body" let-serverBlock> | ||
15 | <tr> | ||
16 | <td>{{ serverBlock.blockedServer.host }}</td> | ||
17 | <td>{{ serverBlock.createdAt }}</td> | ||
18 | <td class="action-cell"> | ||
19 | <button class="unblock-button" (click)="unblockServer(serverBlock)" i18n>Unmute</button> | ||
20 | </td> | ||
21 | </tr> | ||
22 | </ng-template> | ||
23 | </p-table> | ||
diff --git a/client/src/app/+admin/moderation/instance-blocklist/instance-server-blocklist.component.scss b/client/src/app/+admin/moderation/instance-blocklist/instance-server-blocklist.component.scss new file mode 100644 index 000000000..6028b75ea --- /dev/null +++ b/client/src/app/+admin/moderation/instance-blocklist/instance-server-blocklist.component.scss | |||
@@ -0,0 +1,7 @@ | |||
1 | @import '_variables'; | ||
2 | @import '_mixins'; | ||
3 | |||
4 | .unblock-button { | ||
5 | @include peertube-button; | ||
6 | @include grey-button; | ||
7 | } \ No newline at end of file | ||
diff --git a/client/src/app/+admin/moderation/instance-blocklist/instance-server-blocklist.component.ts b/client/src/app/+admin/moderation/instance-blocklist/instance-server-blocklist.component.ts new file mode 100644 index 000000000..9459117a3 --- /dev/null +++ b/client/src/app/+admin/moderation/instance-blocklist/instance-server-blocklist.component.ts | |||
@@ -0,0 +1,60 @@ | |||
1 | import { Component, OnInit } from '@angular/core' | ||
2 | import { NotificationsService } from 'angular2-notifications' | ||
3 | import { I18n } from '@ngx-translate/i18n-polyfill' | ||
4 | import { RestPagination, RestTable } from '@app/shared' | ||
5 | import { SortMeta } from 'primeng/components/common/sortmeta' | ||
6 | import { BlocklistService } from '@app/shared/blocklist' | ||
7 | import { ServerBlock } from '../../../../../../shared' | ||
8 | |||
9 | @Component({ | ||
10 | selector: 'my-instance-server-blocklist', | ||
11 | styleUrls: [ './instance-server-blocklist.component.scss' ], | ||
12 | templateUrl: './instance-server-blocklist.component.html' | ||
13 | }) | ||
14 | export class InstanceServerBlocklistComponent extends RestTable implements OnInit { | ||
15 | blockedAccounts: ServerBlock[] = [] | ||
16 | totalRecords = 0 | ||
17 | rowsPerPage = 10 | ||
18 | sort: SortMeta = { field: 'createdAt', order: -1 } | ||
19 | pagination: RestPagination = { count: this.rowsPerPage, start: 0 } | ||
20 | |||
21 | constructor ( | ||
22 | private notificationsService: NotificationsService, | ||
23 | private blocklistService: BlocklistService, | ||
24 | private i18n: I18n | ||
25 | ) { | ||
26 | super() | ||
27 | } | ||
28 | |||
29 | ngOnInit () { | ||
30 | this.initialize() | ||
31 | } | ||
32 | |||
33 | unblockServer (serverBlock: ServerBlock) { | ||
34 | const host = serverBlock.blockedServer.host | ||
35 | |||
36 | this.blocklistService.unblockServerByInstance(host) | ||
37 | .subscribe( | ||
38 | () => { | ||
39 | this.notificationsService.success( | ||
40 | this.i18n('Success'), | ||
41 | this.i18n('Instance {{host}} unmuted by your instance.', { host }) | ||
42 | ) | ||
43 | |||
44 | this.loadData() | ||
45 | } | ||
46 | ) | ||
47 | } | ||
48 | |||
49 | protected loadData () { | ||
50 | return this.blocklistService.getInstanceServerBlocklist(this.pagination, this.sort) | ||
51 | .subscribe( | ||
52 | resultList => { | ||
53 | this.blockedAccounts = resultList.data | ||
54 | this.totalRecords = resultList.total | ||
55 | }, | ||
56 | |||
57 | err => this.notificationsService.error(this.i18n('Error'), err.message) | ||
58 | ) | ||
59 | } | ||
60 | } | ||
diff --git a/client/src/app/+admin/moderation/moderation.component.html b/client/src/app/+admin/moderation/moderation.component.html index 91e87fcd4..8ec7278ef 100644 --- a/client/src/app/+admin/moderation/moderation.component.html +++ b/client/src/app/+admin/moderation/moderation.component.html | |||
@@ -5,6 +5,10 @@ | |||
5 | <a *ngIf="hasVideoAbusesRight()" i18n routerLink="video-abuses/list" routerLinkActive="active">Video abuses</a> | 5 | <a *ngIf="hasVideoAbusesRight()" i18n routerLink="video-abuses/list" routerLinkActive="active">Video abuses</a> |
6 | 6 | ||
7 | <a *ngIf="hasVideoBlacklistRight()" i18n routerLink="video-blacklist/list" routerLinkActive="active">Blacklisted videos</a> | 7 | <a *ngIf="hasVideoBlacklistRight()" i18n routerLink="video-blacklist/list" routerLinkActive="active">Blacklisted videos</a> |
8 | |||
9 | <a *ngIf="hasAccountsBlacklistRight()" i18n routerLink="blocklist/accounts" routerLinkActive="active">Muted accounts</a> | ||
10 | |||
11 | <a *ngIf="hasServersBlacklistRight()" i18n routerLink="blocklist/servers" routerLinkActive="active">Muted servers</a> | ||
8 | </div> | 12 | </div> |
9 | </div> | 13 | </div> |
10 | 14 | ||
diff --git a/client/src/app/+admin/moderation/moderation.component.ts b/client/src/app/+admin/moderation/moderation.component.ts index 0f4efb970..7f85f920e 100644 --- a/client/src/app/+admin/moderation/moderation.component.ts +++ b/client/src/app/+admin/moderation/moderation.component.ts | |||
@@ -16,4 +16,12 @@ export class ModerationComponent { | |||
16 | hasVideoBlacklistRight () { | 16 | hasVideoBlacklistRight () { |
17 | return this.auth.getUser().hasRight(UserRight.MANAGE_VIDEO_BLACKLIST) | 17 | return this.auth.getUser().hasRight(UserRight.MANAGE_VIDEO_BLACKLIST) |
18 | } | 18 | } |
19 | |||
20 | hasAccountsBlacklistRight () { | ||
21 | return this.auth.getUser().hasRight(UserRight.MANAGE_ACCOUNTS_BLOCKLIST) | ||
22 | } | ||
23 | |||
24 | hasServersBlacklistRight () { | ||
25 | return this.auth.getUser().hasRight(UserRight.MANAGE_SERVERS_BLOCKLIST) | ||
26 | } | ||
19 | } | 27 | } |
diff --git a/client/src/app/+admin/moderation/moderation.routes.ts b/client/src/app/+admin/moderation/moderation.routes.ts index 6d81b9b36..bc6dd49d5 100644 --- a/client/src/app/+admin/moderation/moderation.routes.ts +++ b/client/src/app/+admin/moderation/moderation.routes.ts | |||
@@ -4,6 +4,7 @@ import { UserRightGuard } from '@app/core' | |||
4 | import { VideoAbuseListComponent } from '@app/+admin/moderation/video-abuse-list' | 4 | import { VideoAbuseListComponent } from '@app/+admin/moderation/video-abuse-list' |
5 | import { VideoBlacklistListComponent } from '@app/+admin/moderation/video-blacklist-list' | 5 | import { VideoBlacklistListComponent } from '@app/+admin/moderation/video-blacklist-list' |
6 | import { ModerationComponent } from '@app/+admin/moderation/moderation.component' | 6 | import { ModerationComponent } from '@app/+admin/moderation/moderation.component' |
7 | import { InstanceAccountBlocklistComponent, InstanceServerBlocklistComponent } from '@app/+admin/moderation/instance-blocklist' | ||
7 | 8 | ||
8 | export const ModerationRoutes: Routes = [ | 9 | export const ModerationRoutes: Routes = [ |
9 | { | 10 | { |
@@ -46,6 +47,28 @@ export const ModerationRoutes: Routes = [ | |||
46 | title: 'Blacklisted videos' | 47 | title: 'Blacklisted videos' |
47 | } | 48 | } |
48 | } | 49 | } |
50 | }, | ||
51 | { | ||
52 | path: 'blocklist/accounts', | ||
53 | component: InstanceAccountBlocklistComponent, | ||
54 | canActivate: [ UserRightGuard ], | ||
55 | data: { | ||
56 | userRight: UserRight.MANAGE_ACCOUNTS_BLOCKLIST, | ||
57 | meta: { | ||
58 | title: 'Muted accounts' | ||
59 | } | ||
60 | } | ||
61 | }, | ||
62 | { | ||
63 | path: 'blocklist/servers', | ||
64 | component: InstanceServerBlocklistComponent, | ||
65 | canActivate: [ UserRightGuard ], | ||
66 | data: { | ||
67 | userRight: UserRight.MANAGE_SERVER_REDUNDANCY, | ||
68 | meta: { | ||
69 | title: 'Muted instances' | ||
70 | } | ||
71 | } | ||
49 | } | 72 | } |
50 | ] | 73 | ] |
51 | } | 74 | } |
diff --git a/client/src/app/shared/account/account.model.ts b/client/src/app/shared/account/account.model.ts index 0aba9428a..c5cd2051c 100644 --- a/client/src/app/shared/account/account.model.ts +++ b/client/src/app/shared/account/account.model.ts | |||
@@ -5,8 +5,10 @@ export class Account extends Actor implements ServerAccount { | |||
5 | displayName: string | 5 | displayName: string |
6 | description: string | 6 | description: string |
7 | nameWithHost: string | 7 | nameWithHost: string |
8 | muted: boolean | 8 | mutedByUser: boolean |
9 | mutedServer: boolean | 9 | mutedByInstance: boolean |
10 | mutedServerByUser: boolean | ||
11 | mutedServerByInstance: boolean | ||
10 | 12 | ||
11 | userId?: number | 13 | userId?: number |
12 | 14 | ||
@@ -18,7 +20,9 @@ export class Account extends Actor implements ServerAccount { | |||
18 | this.userId = hash.userId | 20 | this.userId = hash.userId |
19 | this.nameWithHost = Actor.CREATE_BY_STRING(this.name, this.host) | 21 | this.nameWithHost = Actor.CREATE_BY_STRING(this.name, this.host) |
20 | 22 | ||
21 | this.muted = false | 23 | this.mutedByUser = false |
22 | this.mutedServer = false | 24 | this.mutedByInstance = false |
25 | this.mutedServerByUser = false | ||
26 | this.mutedServerByInstance = false | ||
23 | } | 27 | } |
24 | } | 28 | } |
diff --git a/client/src/app/shared/blocklist/account-block.model.ts b/client/src/app/shared/blocklist/account-block.model.ts index 336680f65..e7b433d88 100644 --- a/client/src/app/shared/blocklist/account-block.model.ts +++ b/client/src/app/shared/blocklist/account-block.model.ts | |||
@@ -11,4 +11,4 @@ export class AccountBlock implements AccountBlockServer { | |||
11 | this.blockedAccount = new Account(block.blockedAccount) | 11 | this.blockedAccount = new Account(block.blockedAccount) |
12 | this.createdAt = block.createdAt | 12 | this.createdAt = block.createdAt |
13 | } | 13 | } |
14 | } \ No newline at end of file | 14 | } |
diff --git a/client/src/app/shared/blocklist/blocklist.service.ts b/client/src/app/shared/blocklist/blocklist.service.ts index d9c318258..c1f7312f0 100644 --- a/client/src/app/shared/blocklist/blocklist.service.ts +++ b/client/src/app/shared/blocklist/blocklist.service.ts | |||
@@ -11,6 +11,7 @@ import { AccountBlock } from '@app/shared/blocklist/account-block.model' | |||
11 | @Injectable() | 11 | @Injectable() |
12 | export class BlocklistService { | 12 | export class BlocklistService { |
13 | static BASE_USER_BLOCKLIST_URL = environment.apiUrl + '/api/v1/users/me/blocklist' | 13 | static BASE_USER_BLOCKLIST_URL = environment.apiUrl + '/api/v1/users/me/blocklist' |
14 | static BASE_SERVER_BLOCKLIST_URL = environment.apiUrl + '/api/v1/server/blocklist' | ||
14 | 15 | ||
15 | constructor ( | 16 | constructor ( |
16 | private authHttp: HttpClient, | 17 | private authHttp: HttpClient, |
@@ -73,6 +74,61 @@ export class BlocklistService { | |||
73 | .pipe(catchError(err => this.restExtractor.handleError(err))) | 74 | .pipe(catchError(err => this.restExtractor.handleError(err))) |
74 | } | 75 | } |
75 | 76 | ||
77 | /*********************** Instance -> Account blocklist ***********************/ | ||
78 | |||
79 | getInstanceAccountBlocklist (pagination: RestPagination, sort: SortMeta) { | ||
80 | let params = new HttpParams() | ||
81 | params = this.restService.addRestGetParams(params, pagination, sort) | ||
82 | |||
83 | return this.authHttp.get<ResultList<AccountBlock>>(BlocklistService.BASE_SERVER_BLOCKLIST_URL + '/accounts', { params }) | ||
84 | .pipe( | ||
85 | map(res => this.restExtractor.convertResultListDateToHuman(res)), | ||
86 | map(res => this.restExtractor.applyToResultListData(res, this.formatAccountBlock.bind(this))), | ||
87 | catchError(err => this.restExtractor.handleError(err)) | ||
88 | ) | ||
89 | } | ||
90 | |||
91 | blockAccountByInstance (account: Account) { | ||
92 | const body = { accountName: account.nameWithHost } | ||
93 | |||
94 | return this.authHttp.post(BlocklistService.BASE_SERVER_BLOCKLIST_URL + '/accounts', body) | ||
95 | .pipe(catchError(err => this.restExtractor.handleError(err))) | ||
96 | } | ||
97 | |||
98 | unblockAccountByInstance (account: Account) { | ||
99 | const path = BlocklistService.BASE_SERVER_BLOCKLIST_URL + '/accounts/' + account.nameWithHost | ||
100 | |||
101 | return this.authHttp.delete(path) | ||
102 | .pipe(catchError(err => this.restExtractor.handleError(err))) | ||
103 | } | ||
104 | |||
105 | /*********************** Instance -> Server blocklist ***********************/ | ||
106 | |||
107 | getInstanceServerBlocklist (pagination: RestPagination, sort: SortMeta) { | ||
108 | let params = new HttpParams() | ||
109 | params = this.restService.addRestGetParams(params, pagination, sort) | ||
110 | |||
111 | return this.authHttp.get<ResultList<ServerBlock>>(BlocklistService.BASE_SERVER_BLOCKLIST_URL + '/servers', { params }) | ||
112 | .pipe( | ||
113 | map(res => this.restExtractor.convertResultListDateToHuman(res)), | ||
114 | catchError(err => this.restExtractor.handleError(err)) | ||
115 | ) | ||
116 | } | ||
117 | |||
118 | blockServerByInstance (host: string) { | ||
119 | const body = { host } | ||
120 | |||
121 | return this.authHttp.post(BlocklistService.BASE_SERVER_BLOCKLIST_URL + '/servers', body) | ||
122 | .pipe(catchError(err => this.restExtractor.handleError(err))) | ||
123 | } | ||
124 | |||
125 | unblockServerByInstance (host: string) { | ||
126 | const path = BlocklistService.BASE_SERVER_BLOCKLIST_URL + '/servers/' + host | ||
127 | |||
128 | return this.authHttp.delete(path) | ||
129 | .pipe(catchError(err => this.restExtractor.handleError(err))) | ||
130 | } | ||
131 | |||
76 | private formatAccountBlock (accountBlock: AccountBlockServer) { | 132 | private formatAccountBlock (accountBlock: AccountBlockServer) { |
77 | return new AccountBlock(accountBlock) | 133 | return new AccountBlock(accountBlock) |
78 | } | 134 | } |
diff --git a/client/src/app/shared/blocklist/index.ts b/client/src/app/shared/blocklist/index.ts index 8cf6a55f7..5886ca07e 100644 --- a/client/src/app/shared/blocklist/index.ts +++ b/client/src/app/shared/blocklist/index.ts | |||
@@ -1,2 +1,2 @@ | |||
1 | export * from './blocklist.service' | 1 | export * from './blocklist.service' |
2 | export * from './account-block.model' \ No newline at end of file | 2 | export * from './account-block.model' |
diff --git a/client/src/app/shared/moderation/user-moderation-dropdown.component.ts b/client/src/app/shared/moderation/user-moderation-dropdown.component.ts index 2f4a55f37..908f0b8e0 100644 --- a/client/src/app/shared/moderation/user-moderation-dropdown.component.ts +++ b/client/src/app/shared/moderation/user-moderation-dropdown.component.ts | |||
@@ -26,7 +26,7 @@ export class UserModerationDropdownComponent implements OnChanges { | |||
26 | @Output() userChanged = new EventEmitter() | 26 | @Output() userChanged = new EventEmitter() |
27 | @Output() userDeleted = new EventEmitter() | 27 | @Output() userDeleted = new EventEmitter() |
28 | 28 | ||
29 | userActions: DropdownAction<User>[] = [] | 29 | userActions: DropdownAction<{ user: User, account: Account }>[] = [] |
30 | 30 | ||
31 | constructor ( | 31 | constructor ( |
32 | private authService: AuthService, | 32 | private authService: AuthService, |
@@ -106,7 +106,7 @@ export class UserModerationDropdownComponent implements OnChanges { | |||
106 | this.i18n('Account {{nameWithHost}} muted.', { nameWithHost: account.nameWithHost }) | 106 | this.i18n('Account {{nameWithHost}} muted.', { nameWithHost: account.nameWithHost }) |
107 | ) | 107 | ) |
108 | 108 | ||
109 | this.account.muted = true | 109 | this.account.mutedByUser = true |
110 | this.userChanged.emit() | 110 | this.userChanged.emit() |
111 | }, | 111 | }, |
112 | 112 | ||
@@ -123,7 +123,7 @@ export class UserModerationDropdownComponent implements OnChanges { | |||
123 | this.i18n('Account {{nameWithHost}} unmuted.', { nameWithHost: account.nameWithHost }) | 123 | this.i18n('Account {{nameWithHost}} unmuted.', { nameWithHost: account.nameWithHost }) |
124 | ) | 124 | ) |
125 | 125 | ||
126 | this.account.muted = false | 126 | this.account.mutedByUser = false |
127 | this.userChanged.emit() | 127 | this.userChanged.emit() |
128 | }, | 128 | }, |
129 | 129 | ||
@@ -140,7 +140,7 @@ export class UserModerationDropdownComponent implements OnChanges { | |||
140 | this.i18n('Instance {{host}} muted.', { host }) | 140 | this.i18n('Instance {{host}} muted.', { host }) |
141 | ) | 141 | ) |
142 | 142 | ||
143 | this.account.mutedServer = true | 143 | this.account.mutedServerByUser = true |
144 | this.userChanged.emit() | 144 | this.userChanged.emit() |
145 | }, | 145 | }, |
146 | 146 | ||
@@ -157,7 +157,75 @@ export class UserModerationDropdownComponent implements OnChanges { | |||
157 | this.i18n('Instance {{host}} unmuted.', { host }) | 157 | this.i18n('Instance {{host}} unmuted.', { host }) |
158 | ) | 158 | ) |
159 | 159 | ||
160 | this.account.mutedServer = false | 160 | this.account.mutedServerByUser = false |
161 | this.userChanged.emit() | ||
162 | }, | ||
163 | |||
164 | err => this.notificationsService.error(this.i18n('Error'), err.message) | ||
165 | ) | ||
166 | } | ||
167 | |||
168 | blockAccountByInstance (account: Account) { | ||
169 | this.blocklistService.blockAccountByInstance(account) | ||
170 | .subscribe( | ||
171 | () => { | ||
172 | this.notificationsService.success( | ||
173 | this.i18n('Success'), | ||
174 | this.i18n('Account {{nameWithHost}} muted by the instance.', { nameWithHost: account.nameWithHost }) | ||
175 | ) | ||
176 | |||
177 | this.account.mutedByInstance = true | ||
178 | this.userChanged.emit() | ||
179 | }, | ||
180 | |||
181 | err => this.notificationsService.error(this.i18n('Error'), err.message) | ||
182 | ) | ||
183 | } | ||
184 | |||
185 | unblockAccountByInstance (account: Account) { | ||
186 | this.blocklistService.unblockAccountByInstance(account) | ||
187 | .subscribe( | ||
188 | () => { | ||
189 | this.notificationsService.success( | ||
190 | this.i18n('Success'), | ||
191 | this.i18n('Account {{nameWithHost}} unmuted by the instance.', { nameWithHost: account.nameWithHost }) | ||
192 | ) | ||
193 | |||
194 | this.account.mutedByInstance = false | ||
195 | this.userChanged.emit() | ||
196 | }, | ||
197 | |||
198 | err => this.notificationsService.error(this.i18n('Error'), err.message) | ||
199 | ) | ||
200 | } | ||
201 | |||
202 | blockServerByInstance (host: string) { | ||
203 | this.blocklistService.blockServerByInstance(host) | ||
204 | .subscribe( | ||
205 | () => { | ||
206 | this.notificationsService.success( | ||
207 | this.i18n('Success'), | ||
208 | this.i18n('Instance {{host}} muted by the instance.', { host }) | ||
209 | ) | ||
210 | |||
211 | this.account.mutedServerByInstance = true | ||
212 | this.userChanged.emit() | ||
213 | }, | ||
214 | |||
215 | err => this.notificationsService.error(this.i18n('Error'), err.message) | ||
216 | ) | ||
217 | } | ||
218 | |||
219 | unblockServerByInstance (host: string) { | ||
220 | this.blocklistService.unblockServerByInstance(host) | ||
221 | .subscribe( | ||
222 | () => { | ||
223 | this.notificationsService.success( | ||
224 | this.i18n('Success'), | ||
225 | this.i18n('Instance {{host}} unmuted by the instance.', { host }) | ||
226 | ) | ||
227 | |||
228 | this.account.mutedServerByInstance = false | ||
161 | this.userChanged.emit() | 229 | this.userChanged.emit() |
162 | }, | 230 | }, |
163 | 231 | ||
@@ -189,41 +257,74 @@ export class UserModerationDropdownComponent implements OnChanges { | |||
189 | }, | 257 | }, |
190 | { | 258 | { |
191 | label: this.i18n('Ban'), | 259 | label: this.i18n('Ban'), |
192 | handler: ({ user }) => this.openBanUserModal(user), | 260 | handler: ({ user }: { user: User }) => this.openBanUserModal(user), |
193 | isDisplayed: ({ user }) => !user.muted | 261 | isDisplayed: ({ user }: { user: User }) => !user.blocked |
194 | }, | 262 | }, |
195 | { | 263 | { |
196 | label: this.i18n('Unban'), | 264 | label: this.i18n('Unban'), |
197 | handler: ({ user }) => this.unbanUser(user), | 265 | handler: ({ user }: { user: User }) => this.unbanUser(user), |
198 | isDisplayed: ({ user }) => user.muted | 266 | isDisplayed: ({ user }: { user: User }) => user.blocked |
199 | } | 267 | } |
200 | ]) | 268 | ]) |
201 | } | 269 | } |
202 | 270 | ||
203 | // User actions on accounts/servers | 271 | // Actions on accounts/servers |
204 | if (this.account) { | 272 | if (this.account) { |
273 | // User actions | ||
205 | this.userActions = this.userActions.concat([ | 274 | this.userActions = this.userActions.concat([ |
206 | { | 275 | { |
207 | label: this.i18n('Mute this account'), | 276 | label: this.i18n('Mute this account'), |
208 | isDisplayed: ({ account }) => account.muted === false, | 277 | isDisplayed: ({ account }: { account: Account }) => account.mutedByUser === false, |
209 | handler: ({ account }) => this.blockAccountByUser(account) | 278 | handler: ({ account }: { account: Account }) => this.blockAccountByUser(account) |
210 | }, | 279 | }, |
211 | { | 280 | { |
212 | label: this.i18n('Unmute this account'), | 281 | label: this.i18n('Unmute this account'), |
213 | isDisplayed: ({ account }) => account.muted === true, | 282 | isDisplayed: ({ account }: { account: Account }) => account.mutedByUser === true, |
214 | handler: ({ account }) => this.unblockAccountByUser(account) | 283 | handler: ({ account }: { account: Account }) => this.unblockAccountByUser(account) |
215 | }, | 284 | }, |
216 | { | 285 | { |
217 | label: this.i18n('Mute the instance'), | 286 | label: this.i18n('Mute the instance'), |
218 | isDisplayed: ({ account }) => !account.userId && account.mutedServer === false, | 287 | isDisplayed: ({ account }: { account: Account }) => !account.userId && account.mutedServerByInstance === false, |
219 | handler: ({ account }) => this.blockServerByUser(account.host) | 288 | handler: ({ account }: { account: Account }) => this.blockServerByUser(account.host) |
220 | }, | 289 | }, |
221 | { | 290 | { |
222 | label: this.i18n('Unmute the instance'), | 291 | label: this.i18n('Unmute the instance'), |
223 | isDisplayed: ({ account }) => !account.userId && account.mutedServer === true, | 292 | isDisplayed: ({ account }: { account: Account }) => !account.userId && account.mutedServerByInstance === true, |
224 | handler: ({ account }) => this.unblockServerByUser(account.host) | 293 | handler: ({ account }: { account: Account }) => this.unblockServerByUser(account.host) |
225 | } | 294 | } |
226 | ]) | 295 | ]) |
296 | |||
297 | // Instance actions | ||
298 | if (authUser.hasRight(UserRight.MANAGE_ACCOUNTS_BLOCKLIST)) { | ||
299 | this.userActions = this.userActions.concat([ | ||
300 | { | ||
301 | label: this.i18n('Mute this account by your instance'), | ||
302 | isDisplayed: ({ account }: { account: Account }) => account.mutedByInstance === false, | ||
303 | handler: ({ account }: { account: Account }) => this.blockAccountByInstance(account) | ||
304 | }, | ||
305 | { | ||
306 | label: this.i18n('Unmute this account by your instance'), | ||
307 | isDisplayed: ({ account }: { account: Account }) => account.mutedByInstance === true, | ||
308 | handler: ({ account }: { account: Account }) => this.unblockAccountByInstance(account) | ||
309 | } | ||
310 | ]) | ||
311 | } | ||
312 | |||
313 | // Instance actions | ||
314 | if (authUser.hasRight(UserRight.MANAGE_SERVERS_BLOCKLIST)) { | ||
315 | this.userActions = this.userActions.concat([ | ||
316 | { | ||
317 | label: this.i18n('Mute the instance by your instance'), | ||
318 | isDisplayed: ({ account }: { account: Account }) => !account.userId && account.mutedServerByInstance === false, | ||
319 | handler: ({ account }: { account: Account }) => this.blockServerByInstance(account.host) | ||
320 | }, | ||
321 | { | ||
322 | label: this.i18n('Unmute the instance by your instance'), | ||
323 | isDisplayed: ({ account }: { account: Account }) => !account.userId && account.mutedServerByInstance === true, | ||
324 | handler: ({ account }: { account: Account }) => this.unblockServerByInstance(account.host) | ||
325 | } | ||
326 | ]) | ||
327 | } | ||
227 | } | 328 | } |
228 | } | 329 | } |
229 | } | 330 | } |
diff --git a/client/src/sass/include/_bootstrap-variables.scss b/client/src/sass/include/_bootstrap-variables.scss index ce2532af5..77a20cfe1 100644 --- a/client/src/sass/include/_bootstrap-variables.scss +++ b/client/src/sass/include/_bootstrap-variables.scss | |||
@@ -29,4 +29,6 @@ $input-btn-focus-color: inherit; | |||
29 | $input-focus-border-color: #ced4da; | 29 | $input-focus-border-color: #ced4da; |
30 | 30 | ||
31 | $nav-pills-link-active-bg: #F0F0F0; | 31 | $nav-pills-link-active-bg: #F0F0F0; |
32 | $nav-pills-link-active-color: #000; \ No newline at end of file | 32 | $nav-pills-link-active-color: #000; |
33 | |||
34 | $zindex-dropdown: 10000; \ No newline at end of file | ||
diff --git a/server/models/video/video-comment.ts b/server/models/video/video-comment.ts index 08c6b3ff0..dd6d08139 100644 --- a/server/models/video/video-comment.ts +++ b/server/models/video/video-comment.ts | |||
@@ -294,7 +294,7 @@ export class VideoCommentModel extends Model<VideoCommentModel> { | |||
294 | static async listThreadsForApi (videoId: number, start: number, count: number, sort: string, user?: UserModel) { | 294 | static async listThreadsForApi (videoId: number, start: number, count: number, sort: string, user?: UserModel) { |
295 | const serverActor = await getServerActor() | 295 | const serverActor = await getServerActor() |
296 | const serverAccountId = serverActor.Account.id | 296 | const serverAccountId = serverActor.Account.id |
297 | const userAccountId = user.Account.id | 297 | const userAccountId = user ? user.Account.id : undefined |
298 | 298 | ||
299 | const query = { | 299 | const query = { |
300 | offset: start, | 300 | offset: start, |
@@ -330,7 +330,7 @@ export class VideoCommentModel extends Model<VideoCommentModel> { | |||
330 | static async listThreadCommentsForApi (videoId: number, threadId: number, user?: UserModel) { | 330 | static async listThreadCommentsForApi (videoId: number, threadId: number, user?: UserModel) { |
331 | const serverActor = await getServerActor() | 331 | const serverActor = await getServerActor() |
332 | const serverAccountId = serverActor.Account.id | 332 | const serverAccountId = serverActor.Account.id |
333 | const userAccountId = user.Account.id | 333 | const userAccountId = user ? user.Account.id : undefined |
334 | 334 | ||
335 | const query = { | 335 | const query = { |
336 | order: [ [ 'createdAt', 'ASC' ], [ 'updatedAt', 'ASC' ] ], | 336 | order: [ [ 'createdAt', 'ASC' ], [ 'updatedAt', 'ASC' ] ], |
diff --git a/server/models/video/video.ts b/server/models/video/video.ts index eab99cba7..6c183933b 100644 --- a/server/models/video/video.ts +++ b/server/models/video/video.ts | |||
@@ -1255,9 +1255,11 @@ export class VideoModel extends Model<VideoModel> { | |||
1255 | 1255 | ||
1256 | // threshold corresponds to how many video the field should have to be returned | 1256 | // threshold corresponds to how many video the field should have to be returned |
1257 | static async getRandomFieldSamples (field: 'category' | 'channelId', threshold: number, count: number) { | 1257 | static async getRandomFieldSamples (field: 'category' | 'channelId', threshold: number, count: number) { |
1258 | const actorId = (await getServerActor()).id | 1258 | const serverActor = await getServerActor() |
1259 | const actorId = serverActor.id | ||
1259 | 1260 | ||
1260 | const scopeOptions = { | 1261 | const scopeOptions: AvailableForListIDsOptions = { |
1262 | serverAccountId: serverActor.Account.id, | ||
1261 | actorId, | 1263 | actorId, |
1262 | includeLocalVideos: true | 1264 | includeLocalVideos: true |
1263 | } | 1265 | } |
diff --git a/server/tests/api/users/blocklist.ts b/server/tests/api/users/blocklist.ts index 99fe04b8c..eed4b9f3e 100644 --- a/server/tests/api/users/blocklist.ts +++ b/server/tests/api/users/blocklist.ts | |||
@@ -14,7 +14,7 @@ import { | |||
14 | userLogin | 14 | userLogin |
15 | } from '../../utils/index' | 15 | } from '../../utils/index' |
16 | import { setAccessTokensToServers } from '../../utils/users/login' | 16 | import { setAccessTokensToServers } from '../../utils/users/login' |
17 | import { getVideosListWithToken } from '../../utils/videos/videos' | 17 | import { getVideosListWithToken, getVideosList } from '../../utils/videos/videos' |
18 | import { | 18 | import { |
19 | addVideoCommentReply, | 19 | addVideoCommentReply, |
20 | addVideoCommentThread, | 20 | addVideoCommentThread, |
@@ -41,9 +41,17 @@ import { | |||
41 | const expect = chai.expect | 41 | const expect = chai.expect |
42 | 42 | ||
43 | async function checkAllVideos (url: string, token: string) { | 43 | async function checkAllVideos (url: string, token: string) { |
44 | const res = await getVideosListWithToken(url, token) | 44 | { |
45 | const res = await getVideosListWithToken(url, token) | ||
45 | 46 | ||
46 | expect(res.body.data).to.have.lengthOf(4) | 47 | expect(res.body.data).to.have.lengthOf(4) |
48 | } | ||
49 | |||
50 | { | ||
51 | const res = await getVideosList(url) | ||
52 | |||
53 | expect(res.body.data).to.have.lengthOf(4) | ||
54 | } | ||
47 | } | 55 | } |
48 | 56 | ||
49 | async function checkAllComments (url: string, token: string, videoUUID: string) { | 57 | async function checkAllComments (url: string, token: string, videoUUID: string) { |
@@ -444,16 +452,19 @@ describe('Test blocklist', function () { | |||
444 | 452 | ||
445 | it('Should hide its videos', async function () { | 453 | it('Should hide its videos', async function () { |
446 | for (const token of [ userModeratorToken, servers[ 0 ].accessToken ]) { | 454 | for (const token of [ userModeratorToken, servers[ 0 ].accessToken ]) { |
447 | const res = await getVideosListWithToken(servers[ 0 ].url, token) | 455 | const res1 = await getVideosList(servers[ 0 ].url) |
456 | const res2 = await getVideosListWithToken(servers[ 0 ].url, token) | ||
448 | 457 | ||
449 | const videos: Video[] = res.body.data | 458 | for (const res of [ res1, res2 ]) { |
450 | expect(videos).to.have.lengthOf(2) | 459 | const videos: Video[] = res.body.data |
460 | expect(videos).to.have.lengthOf(2) | ||
451 | 461 | ||
452 | const v1 = videos.find(v => v.name === 'video user 2') | 462 | const v1 = videos.find(v => v.name === 'video user 2') |
453 | const v2 = videos.find(v => v.name === 'video server 2') | 463 | const v2 = videos.find(v => v.name === 'video server 2') |
454 | 464 | ||
455 | expect(v1).to.be.undefined | 465 | expect(v1).to.be.undefined |
456 | expect(v2).to.be.undefined | 466 | expect(v2).to.be.undefined |
467 | } | ||
457 | } | 468 | } |
458 | }) | 469 | }) |
459 | 470 | ||