aboutsummaryrefslogtreecommitdiffhomepage
path: root/client/src/app/shared
diff options
context:
space:
mode:
Diffstat (limited to 'client/src/app/shared')
-rw-r--r--client/src/app/shared/account/account.model.ts5
-rw-r--r--client/src/app/shared/blocklist/account-block.model.ts14
-rw-r--r--client/src/app/shared/blocklist/blocklist.service.ts79
-rw-r--r--client/src/app/shared/blocklist/index.ts2
-rw-r--r--client/src/app/shared/buttons/action-dropdown.component.html6
-rw-r--r--client/src/app/shared/buttons/action-dropdown.component.scss5
-rw-r--r--client/src/app/shared/moderation/user-moderation-dropdown.component.html7
-rw-r--r--client/src/app/shared/moderation/user-moderation-dropdown.component.ts121
-rw-r--r--client/src/app/shared/shared.module.ts2
9 files changed, 226 insertions, 15 deletions
diff --git a/client/src/app/shared/account/account.model.ts b/client/src/app/shared/account/account.model.ts
index 42f2cfeaf..0aba9428a 100644
--- a/client/src/app/shared/account/account.model.ts
+++ b/client/src/app/shared/account/account.model.ts
@@ -5,6 +5,8 @@ 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
9 mutedServer: boolean
8 10
9 userId?: number 11 userId?: number
10 12
@@ -15,5 +17,8 @@ export class Account extends Actor implements ServerAccount {
15 this.description = hash.description 17 this.description = hash.description
16 this.userId = hash.userId 18 this.userId = hash.userId
17 this.nameWithHost = Actor.CREATE_BY_STRING(this.name, this.host) 19 this.nameWithHost = Actor.CREATE_BY_STRING(this.name, this.host)
20
21 this.muted = false
22 this.mutedServer = false
18 } 23 }
19} 24}
diff --git a/client/src/app/shared/blocklist/account-block.model.ts b/client/src/app/shared/blocklist/account-block.model.ts
new file mode 100644
index 000000000..336680f65
--- /dev/null
+++ b/client/src/app/shared/blocklist/account-block.model.ts
@@ -0,0 +1,14 @@
1import { AccountBlock as AccountBlockServer } from '../../../../../shared'
2import { Account } from '../account/account.model'
3
4export class AccountBlock implements AccountBlockServer {
5 byAccount: Account
6 blockedAccount: Account
7 createdAt: Date | string
8
9 constructor (block: AccountBlockServer) {
10 this.byAccount = new Account(block.byAccount)
11 this.blockedAccount = new Account(block.blockedAccount)
12 this.createdAt = block.createdAt
13 }
14} \ No newline at end of file
diff --git a/client/src/app/shared/blocklist/blocklist.service.ts b/client/src/app/shared/blocklist/blocklist.service.ts
new file mode 100644
index 000000000..d9c318258
--- /dev/null
+++ b/client/src/app/shared/blocklist/blocklist.service.ts
@@ -0,0 +1,79 @@
1import { Injectable } from '@angular/core'
2import { environment } from '../../../environments/environment'
3import { HttpClient, HttpParams } from '@angular/common/http'
4import { RestExtractor, RestPagination, RestService } from '../rest'
5import { SortMeta } from 'primeng/api'
6import { catchError, map } from 'rxjs/operators'
7import { AccountBlock as AccountBlockServer, ResultList, ServerBlock } from '../../../../../shared'
8import { Account } from '@app/shared/account/account.model'
9import { AccountBlock } from '@app/shared/blocklist/account-block.model'
10
11@Injectable()
12export class BlocklistService {
13 static BASE_USER_BLOCKLIST_URL = environment.apiUrl + '/api/v1/users/me/blocklist'
14
15 constructor (
16 private authHttp: HttpClient,
17 private restExtractor: RestExtractor,
18 private restService: RestService
19 ) { }
20
21 /*********************** User -> Account blocklist ***********************/
22
23 getUserAccountBlocklist (pagination: RestPagination, sort: SortMeta) {
24 let params = new HttpParams()
25 params = this.restService.addRestGetParams(params, pagination, sort)
26
27 return this.authHttp.get<ResultList<AccountBlock>>(BlocklistService.BASE_USER_BLOCKLIST_URL + '/accounts', { params })
28 .pipe(
29 map(res => this.restExtractor.convertResultListDateToHuman(res)),
30 map(res => this.restExtractor.applyToResultListData(res, this.formatAccountBlock.bind(this))),
31 catchError(err => this.restExtractor.handleError(err))
32 )
33 }
34
35 blockAccountByUser (account: Account) {
36 const body = { accountName: account.nameWithHost }
37
38 return this.authHttp.post(BlocklistService.BASE_USER_BLOCKLIST_URL + '/accounts', body)
39 .pipe(catchError(err => this.restExtractor.handleError(err)))
40 }
41
42 unblockAccountByUser (account: Account) {
43 const path = BlocklistService.BASE_USER_BLOCKLIST_URL + '/accounts/' + account.nameWithHost
44
45 return this.authHttp.delete(path)
46 .pipe(catchError(err => this.restExtractor.handleError(err)))
47 }
48
49 /*********************** User -> Server blocklist ***********************/
50
51 getUserServerBlocklist (pagination: RestPagination, sort: SortMeta) {
52 let params = new HttpParams()
53 params = this.restService.addRestGetParams(params, pagination, sort)
54
55 return this.authHttp.get<ResultList<ServerBlock>>(BlocklistService.BASE_USER_BLOCKLIST_URL + '/servers', { params })
56 .pipe(
57 map(res => this.restExtractor.convertResultListDateToHuman(res)),
58 catchError(err => this.restExtractor.handleError(err))
59 )
60 }
61
62 blockServerByUser (host: string) {
63 const body = { host }
64
65 return this.authHttp.post(BlocklistService.BASE_USER_BLOCKLIST_URL + '/servers', body)
66 .pipe(catchError(err => this.restExtractor.handleError(err)))
67 }
68
69 unblockServerByUser (host: string) {
70 const path = BlocklistService.BASE_USER_BLOCKLIST_URL + '/servers/' + host
71
72 return this.authHttp.delete(path)
73 .pipe(catchError(err => this.restExtractor.handleError(err)))
74 }
75
76 private formatAccountBlock (accountBlock: AccountBlockServer) {
77 return new AccountBlock(accountBlock)
78 }
79}
diff --git a/client/src/app/shared/blocklist/index.ts b/client/src/app/shared/blocklist/index.ts
new file mode 100644
index 000000000..8cf6a55f7
--- /dev/null
+++ b/client/src/app/shared/blocklist/index.ts
@@ -0,0 +1,2 @@
1export * from './blocklist.service'
2export * from './account-block.model' \ No newline at end of file
diff --git a/client/src/app/shared/buttons/action-dropdown.component.html b/client/src/app/shared/buttons/action-dropdown.component.html
index 111627424..48230d6d8 100644
--- a/client/src/app/shared/buttons/action-dropdown.component.html
+++ b/client/src/app/shared/buttons/action-dropdown.component.html
@@ -9,13 +9,13 @@
9 9
10 <div ngbDropdownMenu class="dropdown-menu"> 10 <div ngbDropdownMenu class="dropdown-menu">
11 <ng-container *ngFor="let action of actions"> 11 <ng-container *ngFor="let action of actions">
12 <div class="dropdown-item" *ngIf="action.isDisplayed === undefined || action.isDisplayed(entry) === true"> 12 <ng-container *ngIf="action.isDisplayed === undefined || action.isDisplayed(entry) === true">
13 <a *ngIf="action.linkBuilder" class="dropdown-item" [routerLink]="action.linkBuilder(entry)">{{ action.label }}</a> 13 <a *ngIf="action.linkBuilder" class="dropdown-item" [routerLink]="action.linkBuilder(entry)">{{ action.label }}</a>
14 14
15 <span *ngIf="!action.linkBuilder" class="custom-action" class="dropdown-item" (click)="action.handler(entry)" role="button"> 15 <span *ngIf="!action.linkBuilder" class="custom-action dropdown-item" (click)="action.handler(entry)" role="button">
16 {{ action.label }} 16 {{ action.label }}
17 </span> 17 </span>
18 </div> 18 </ng-container>
19 </ng-container> 19 </ng-container>
20 </div> 20 </div>
21</div> \ No newline at end of file 21</div> \ No newline at end of file
diff --git a/client/src/app/shared/buttons/action-dropdown.component.scss b/client/src/app/shared/buttons/action-dropdown.component.scss
index 0a9aa7b04..92c4d1d2c 100644
--- a/client/src/app/shared/buttons/action-dropdown.component.scss
+++ b/client/src/app/shared/buttons/action-dropdown.component.scss
@@ -46,5 +46,10 @@
46 .dropdown-item { 46 .dropdown-item {
47 cursor: pointer; 47 cursor: pointer;
48 color: #000 !important; 48 color: #000 !important;
49
50 a, span {
51 display: block;
52 width: 100%;
53 }
49 } 54 }
50} \ No newline at end of file 55} \ No newline at end of file
diff --git a/client/src/app/shared/moderation/user-moderation-dropdown.component.html b/client/src/app/shared/moderation/user-moderation-dropdown.component.html
index 01db7cd4a..7367a7e59 100644
--- a/client/src/app/shared/moderation/user-moderation-dropdown.component.html
+++ b/client/src/app/shared/moderation/user-moderation-dropdown.component.html
@@ -1,5 +1,8 @@
1<ng-container *ngIf="user && userActions.length !== 0"> 1<ng-container *ngIf="userActions.length !== 0">
2 <my-user-ban-modal #userBanModal (userBanned)="onUserBanned()"></my-user-ban-modal> 2 <my-user-ban-modal #userBanModal (userBanned)="onUserBanned()"></my-user-ban-modal>
3 3
4 <my-action-dropdown [actions]="userActions" [entry]="user" [buttonSize]="buttonSize" [placement]="placement"></my-action-dropdown> 4 <my-action-dropdown
5 [actions]="userActions" [entry]="{ user: user, account: account }"
6 [buttonSize]="buttonSize" [placement]="placement"
7 ></my-action-dropdown>
5</ng-container> \ No newline at end of file 8</ng-container> \ No newline at end of file
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 105c99d8b..2f4a55f37 100644
--- a/client/src/app/shared/moderation/user-moderation-dropdown.component.ts
+++ b/client/src/app/shared/moderation/user-moderation-dropdown.component.ts
@@ -1,4 +1,4 @@
1import { Component, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core' 1import { Component, EventEmitter, Input, OnChanges, Output, ViewChild } from '@angular/core'
2import { NotificationsService } from 'angular2-notifications' 2import { NotificationsService } from 'angular2-notifications'
3import { I18n } from '@ngx-translate/i18n-polyfill' 3import { I18n } from '@ngx-translate/i18n-polyfill'
4import { DropdownAction } from '@app/shared/buttons/action-dropdown.component' 4import { DropdownAction } from '@app/shared/buttons/action-dropdown.component'
@@ -6,16 +6,20 @@ import { UserBanModalComponent } from '@app/shared/moderation/user-ban-modal.com
6import { UserService } from '@app/shared/users' 6import { UserService } from '@app/shared/users'
7import { AuthService, ConfirmService } from '@app/core' 7import { AuthService, ConfirmService } from '@app/core'
8import { User, UserRight } from '../../../../../shared/models/users' 8import { User, UserRight } from '../../../../../shared/models/users'
9import { Account } from '@app/shared/account/account.model'
10import { BlocklistService } from '@app/shared/blocklist'
9 11
10@Component({ 12@Component({
11 selector: 'my-user-moderation-dropdown', 13 selector: 'my-user-moderation-dropdown',
12 templateUrl: './user-moderation-dropdown.component.html', 14 templateUrl: './user-moderation-dropdown.component.html',
13 styleUrls: [ './user-moderation-dropdown.component.scss' ] 15 styleUrls: [ './user-moderation-dropdown.component.scss' ]
14}) 16})
15export class UserModerationDropdownComponent implements OnInit { 17export class UserModerationDropdownComponent implements OnChanges {
16 @ViewChild('userBanModal') userBanModal: UserBanModalComponent 18 @ViewChild('userBanModal') userBanModal: UserBanModalComponent
17 19
18 @Input() user: User 20 @Input() user: User
21 @Input() account: Account
22
19 @Input() buttonSize: 'normal' | 'small' = 'normal' 23 @Input() buttonSize: 'normal' | 'small' = 'normal'
20 @Input() placement = 'left' 24 @Input() placement = 'left'
21 25
@@ -29,10 +33,11 @@ export class UserModerationDropdownComponent implements OnInit {
29 private notificationsService: NotificationsService, 33 private notificationsService: NotificationsService,
30 private confirmService: ConfirmService, 34 private confirmService: ConfirmService,
31 private userService: UserService, 35 private userService: UserService,
36 private blocklistService: BlocklistService,
32 private i18n: I18n 37 private i18n: I18n
33 ) { } 38 ) { }
34 39
35 ngOnInit () { 40 ngOnChanges () {
36 this.buildActions() 41 this.buildActions()
37 } 42 }
38 43
@@ -92,6 +97,74 @@ export class UserModerationDropdownComponent implements OnInit {
92 ) 97 )
93 } 98 }
94 99
100 blockAccountByUser (account: Account) {
101 this.blocklistService.blockAccountByUser(account)
102 .subscribe(
103 () => {
104 this.notificationsService.success(
105 this.i18n('Success'),
106 this.i18n('Account {{nameWithHost}} muted.', { nameWithHost: account.nameWithHost })
107 )
108
109 this.account.muted = true
110 this.userChanged.emit()
111 },
112
113 err => this.notificationsService.error(this.i18n('Error'), err.message)
114 )
115 }
116
117 unblockAccountByUser (account: Account) {
118 this.blocklistService.unblockAccountByUser(account)
119 .subscribe(
120 () => {
121 this.notificationsService.success(
122 this.i18n('Success'),
123 this.i18n('Account {{nameWithHost}} unmuted.', { nameWithHost: account.nameWithHost })
124 )
125
126 this.account.muted = false
127 this.userChanged.emit()
128 },
129
130 err => this.notificationsService.error(this.i18n('Error'), err.message)
131 )
132 }
133
134 blockServerByUser (host: string) {
135 this.blocklistService.blockServerByUser(host)
136 .subscribe(
137 () => {
138 this.notificationsService.success(
139 this.i18n('Success'),
140 this.i18n('Instance {{host}} muted.', { host })
141 )
142
143 this.account.mutedServer = true
144 this.userChanged.emit()
145 },
146
147 err => this.notificationsService.error(this.i18n('Error'), err.message)
148 )
149 }
150
151 unblockServerByUser (host: string) {
152 this.blocklistService.unblockServerByUser(host)
153 .subscribe(
154 () => {
155 this.notificationsService.success(
156 this.i18n('Success'),
157 this.i18n('Instance {{host}} unmuted.', { host })
158 )
159
160 this.account.mutedServer = false
161 this.userChanged.emit()
162 },
163
164 err => this.notificationsService.error(this.i18n('Error'), err.message)
165 )
166 }
167
95 getRouterUserEditLink (user: User) { 168 getRouterUserEditLink (user: User) {
96 return [ '/admin', 'users', 'update', user.id ] 169 return [ '/admin', 'users', 'update', user.id ]
97 } 170 }
@@ -102,25 +175,53 @@ export class UserModerationDropdownComponent implements OnInit {
102 if (this.authService.isLoggedIn()) { 175 if (this.authService.isLoggedIn()) {
103 const authUser = this.authService.getUser() 176 const authUser = this.authService.getUser()
104 177
105 if (authUser.hasRight(UserRight.MANAGE_USERS)) { 178 if (this.user && authUser.id === this.user.id) return
179
180 if (this.user && authUser.hasRight(UserRight.MANAGE_USERS)) {
106 this.userActions = this.userActions.concat([ 181 this.userActions = this.userActions.concat([
107 { 182 {
108 label: this.i18n('Edit'), 183 label: this.i18n('Edit'),
109 linkBuilder: this.getRouterUserEditLink 184 linkBuilder: ({ user }) => this.getRouterUserEditLink(user)
110 }, 185 },
111 { 186 {
112 label: this.i18n('Delete'), 187 label: this.i18n('Delete'),
113 handler: user => this.removeUser(user) 188 handler: ({ user }) => this.removeUser(user)
114 }, 189 },
115 { 190 {
116 label: this.i18n('Ban'), 191 label: this.i18n('Ban'),
117 handler: user => this.openBanUserModal(user), 192 handler: ({ user }) => this.openBanUserModal(user),
118 isDisplayed: user => !user.blocked 193 isDisplayed: ({ user }) => !user.muted
119 }, 194 },
120 { 195 {
121 label: this.i18n('Unban'), 196 label: this.i18n('Unban'),
122 handler: user => this.unbanUser(user), 197 handler: ({ user }) => this.unbanUser(user),
123 isDisplayed: user => user.blocked 198 isDisplayed: ({ user }) => user.muted
199 }
200 ])
201 }
202
203 // User actions on accounts/servers
204 if (this.account) {
205 this.userActions = this.userActions.concat([
206 {
207 label: this.i18n('Mute this account'),
208 isDisplayed: ({ account }) => account.muted === false,
209 handler: ({ account }) => this.blockAccountByUser(account)
210 },
211 {
212 label: this.i18n('Unmute this account'),
213 isDisplayed: ({ account }) => account.muted === true,
214 handler: ({ account }) => this.unblockAccountByUser(account)
215 },
216 {
217 label: this.i18n('Mute the instance'),
218 isDisplayed: ({ account }) => !account.userId && account.mutedServer === false,
219 handler: ({ account }) => this.blockServerByUser(account.host)
220 },
221 {
222 label: this.i18n('Unmute the instance'),
223 isDisplayed: ({ account }) => !account.userId && account.mutedServer === true,
224 handler: ({ account }) => this.unblockServerByUser(account.host)
124 } 225 }
125 ]) 226 ])
126 } 227 }
diff --git a/client/src/app/shared/shared.module.ts b/client/src/app/shared/shared.module.ts
index 9647a7966..40e05fcc7 100644
--- a/client/src/app/shared/shared.module.ts
+++ b/client/src/app/shared/shared.module.ts
@@ -58,6 +58,7 @@ import { InstanceFeaturesTableComponent } from '@app/shared/instance/instance-fe
58import { OverviewService } from '@app/shared/overview' 58import { OverviewService } from '@app/shared/overview'
59import { UserBanModalComponent } from '@app/shared/moderation' 59import { UserBanModalComponent } from '@app/shared/moderation'
60import { UserModerationDropdownComponent } from '@app/shared/moderation/user-moderation-dropdown.component' 60import { UserModerationDropdownComponent } from '@app/shared/moderation/user-moderation-dropdown.component'
61import { BlocklistService } from '@app/shared/blocklist'
61 62
62@NgModule({ 63@NgModule({
63 imports: [ 64 imports: [
@@ -172,6 +173,7 @@ import { UserModerationDropdownComponent } from '@app/shared/moderation/user-mod
172 OverviewService, 173 OverviewService,
173 VideoChangeOwnershipValidatorsService, 174 VideoChangeOwnershipValidatorsService,
174 VideoAcceptOwnershipValidatorsService, 175 VideoAcceptOwnershipValidatorsService,
176 BlocklistService,
175 177
176 I18nPrimengCalendarService, 178 I18nPrimengCalendarService,
177 ScreenService, 179 ScreenService,