diff options
21 files changed, 342 insertions, 276 deletions
diff --git a/client/src/app/+admin/admin.module.ts b/client/src/app/+admin/admin.module.ts index e0dea3ba5..c672fa280 100644 --- a/client/src/app/+admin/admin.module.ts +++ b/client/src/app/+admin/admin.module.ts | |||
@@ -10,6 +10,7 @@ import { SharedGlobalIconModule } from '@app/shared/shared-icons' | |||
10 | import { SharedMainModule } from '@app/shared/shared-main' | 10 | import { SharedMainModule } from '@app/shared/shared-main' |
11 | import { SharedModerationModule } from '@app/shared/shared-moderation' | 11 | import { SharedModerationModule } from '@app/shared/shared-moderation' |
12 | import { SharedTablesModule } from '@app/shared/shared-tables' | 12 | import { SharedTablesModule } from '@app/shared/shared-tables' |
13 | import { SharedUsersModule } from '@app/shared/shared-users' | ||
13 | import { SharedVideoCommentModule } from '@app/shared/shared-video-comment' | 14 | import { SharedVideoCommentModule } from '@app/shared/shared-video-comment' |
14 | import { SharedVideoMiniatureModule } from '@app/shared/shared-video-miniature' | 15 | import { SharedVideoMiniatureModule } from '@app/shared/shared-video-miniature' |
15 | import { AdminRoutingModule } from './admin-routing.module' | 16 | import { AdminRoutingModule } from './admin-routing.module' |
@@ -67,6 +68,7 @@ import { JobsComponent } from './system/jobs/jobs.component' | |||
67 | SharedCustomMarkupModule, | 68 | SharedCustomMarkupModule, |
68 | SharedVideoMiniatureModule, | 69 | SharedVideoMiniatureModule, |
69 | SharedTablesModule, | 70 | SharedTablesModule, |
71 | SharedUsersModule, | ||
70 | 72 | ||
71 | TableModule, | 73 | TableModule, |
72 | ChartModule | 74 | ChartModule |
diff --git a/client/src/app/+admin/overview/users/user-edit/user-create.component.ts b/client/src/app/+admin/overview/users/user-edit/user-create.component.ts index b61b22fd0..1713e06ce 100644 --- a/client/src/app/+admin/overview/users/user-edit/user-create.component.ts +++ b/client/src/app/+admin/overview/users/user-edit/user-create.component.ts | |||
@@ -1,7 +1,7 @@ | |||
1 | import { Component, OnInit } from '@angular/core' | 1 | import { Component, OnInit } from '@angular/core' |
2 | import { Router } from '@angular/router' | 2 | import { Router } from '@angular/router' |
3 | import { ConfigService } from '@app/+admin/config/shared/config.service' | 3 | import { ConfigService } from '@app/+admin/config/shared/config.service' |
4 | import { AuthService, Notifier, ScreenService, ServerService, UserService } from '@app/core' | 4 | import { AuthService, Notifier, ScreenService, ServerService } from '@app/core' |
5 | import { | 5 | import { |
6 | USER_CHANNEL_NAME_VALIDATOR, | 6 | USER_CHANNEL_NAME_VALIDATOR, |
7 | USER_EMAIL_VALIDATOR, | 7 | USER_EMAIL_VALIDATOR, |
@@ -13,6 +13,7 @@ import { | |||
13 | USER_VIDEO_QUOTA_VALIDATOR | 13 | USER_VIDEO_QUOTA_VALIDATOR |
14 | } from '@app/shared/form-validators/user-validators' | 14 | } from '@app/shared/form-validators/user-validators' |
15 | import { FormValidatorService } from '@app/shared/shared-forms' | 15 | import { FormValidatorService } from '@app/shared/shared-forms' |
16 | import { UserAdminService } from '@app/shared/shared-users' | ||
16 | import { UserCreate, UserRole } from '@shared/models' | 17 | import { UserCreate, UserRole } from '@shared/models' |
17 | import { UserEdit } from './user-edit' | 18 | import { UserEdit } from './user-edit' |
18 | 19 | ||
@@ -32,7 +33,7 @@ export class UserCreateComponent extends UserEdit implements OnInit { | |||
32 | protected auth: AuthService, | 33 | protected auth: AuthService, |
33 | private router: Router, | 34 | private router: Router, |
34 | private notifier: Notifier, | 35 | private notifier: Notifier, |
35 | private userService: UserService | 36 | private userAdminService: UserAdminService |
36 | ) { | 37 | ) { |
37 | super() | 38 | super() |
38 | 39 | ||
@@ -71,7 +72,7 @@ export class UserCreateComponent extends UserEdit implements OnInit { | |||
71 | userCreate.videoQuota = parseInt(this.form.value['videoQuota'], 10) | 72 | userCreate.videoQuota = parseInt(this.form.value['videoQuota'], 10) |
72 | userCreate.videoQuotaDaily = parseInt(this.form.value['videoQuotaDaily'], 10) | 73 | userCreate.videoQuotaDaily = parseInt(this.form.value['videoQuotaDaily'], 10) |
73 | 74 | ||
74 | this.userService.addUser(userCreate) | 75 | this.userAdminService.addUser(userCreate) |
75 | .subscribe({ | 76 | .subscribe({ |
76 | next: () => { | 77 | next: () => { |
77 | this.notifier.success($localize`User ${userCreate.username} created.`) | 78 | this.notifier.success($localize`User ${userCreate.username} created.`) |
diff --git a/client/src/app/+admin/overview/users/user-edit/user-password.component.ts b/client/src/app/+admin/overview/users/user-edit/user-password.component.ts index 42bf20de1..8999d1f00 100644 --- a/client/src/app/+admin/overview/users/user-edit/user-password.component.ts +++ b/client/src/app/+admin/overview/users/user-edit/user-password.component.ts | |||
@@ -1,7 +1,8 @@ | |||
1 | import { Component, Input, OnInit } from '@angular/core' | 1 | import { Component, Input, OnInit } from '@angular/core' |
2 | import { Notifier, UserService } from '@app/core' | 2 | import { Notifier } from '@app/core' |
3 | import { USER_PASSWORD_VALIDATOR } from '@app/shared/form-validators/user-validators' | 3 | import { USER_PASSWORD_VALIDATOR } from '@app/shared/form-validators/user-validators' |
4 | import { FormReactive, FormValidatorService } from '@app/shared/shared-forms' | 4 | import { FormReactive, FormValidatorService } from '@app/shared/shared-forms' |
5 | import { UserAdminService } from '@app/shared/shared-users' | ||
5 | import { UserUpdate } from '@shared/models' | 6 | import { UserUpdate } from '@shared/models' |
6 | 7 | ||
7 | @Component({ | 8 | @Component({ |
@@ -19,7 +20,7 @@ export class UserPasswordComponent extends FormReactive implements OnInit { | |||
19 | constructor ( | 20 | constructor ( |
20 | protected formValidatorService: FormValidatorService, | 21 | protected formValidatorService: FormValidatorService, |
21 | private notifier: Notifier, | 22 | private notifier: Notifier, |
22 | private userService: UserService | 23 | private userAdminService: UserAdminService |
23 | ) { | 24 | ) { |
24 | super() | 25 | super() |
25 | } | 26 | } |
@@ -35,7 +36,7 @@ export class UserPasswordComponent extends FormReactive implements OnInit { | |||
35 | 36 | ||
36 | const userUpdate: UserUpdate = this.form.value | 37 | const userUpdate: UserUpdate = this.form.value |
37 | 38 | ||
38 | this.userService.updateUser(this.userId, userUpdate) | 39 | this.userAdminService.updateUser(this.userId, userUpdate) |
39 | .subscribe({ | 40 | .subscribe({ |
40 | next: () => this.notifier.success($localize`Password changed for user ${this.username}.`), | 41 | next: () => this.notifier.success($localize`Password changed for user ${this.username}.`), |
41 | 42 | ||
diff --git a/client/src/app/+admin/overview/users/user-edit/user-update.component.ts b/client/src/app/+admin/overview/users/user-edit/user-update.component.ts index 42599a17e..bab288a67 100644 --- a/client/src/app/+admin/overview/users/user-edit/user-update.component.ts +++ b/client/src/app/+admin/overview/users/user-edit/user-update.component.ts | |||
@@ -10,6 +10,7 @@ import { | |||
10 | USER_VIDEO_QUOTA_VALIDATOR | 10 | USER_VIDEO_QUOTA_VALIDATOR |
11 | } from '@app/shared/form-validators/user-validators' | 11 | } from '@app/shared/form-validators/user-validators' |
12 | import { FormValidatorService } from '@app/shared/shared-forms' | 12 | import { FormValidatorService } from '@app/shared/shared-forms' |
13 | import { UserAdminService } from '@app/shared/shared-users' | ||
13 | import { User as UserType, UserAdminFlag, UserRole, UserUpdate } from '@shared/models' | 14 | import { User as UserType, UserAdminFlag, UserRole, UserUpdate } from '@shared/models' |
14 | import { UserEdit } from './user-edit' | 15 | import { UserEdit } from './user-edit' |
15 | 16 | ||
@@ -32,7 +33,8 @@ export class UserUpdateComponent extends UserEdit implements OnInit, OnDestroy { | |||
32 | private route: ActivatedRoute, | 33 | private route: ActivatedRoute, |
33 | private router: Router, | 34 | private router: Router, |
34 | private notifier: Notifier, | 35 | private notifier: Notifier, |
35 | private userService: UserService | 36 | private userService: UserService, |
37 | private userAdminService: UserAdminService | ||
36 | ) { | 38 | ) { |
37 | super() | 39 | super() |
38 | 40 | ||
@@ -86,7 +88,7 @@ export class UserUpdateComponent extends UserEdit implements OnInit, OnDestroy { | |||
86 | 88 | ||
87 | if (userUpdate.pluginAuth === 'null') userUpdate.pluginAuth = null | 89 | if (userUpdate.pluginAuth === 'null') userUpdate.pluginAuth = null |
88 | 90 | ||
89 | this.userService.updateUser(this.user.id, userUpdate) | 91 | this.userAdminService.updateUser(this.user.id, userUpdate) |
90 | .subscribe({ | 92 | .subscribe({ |
91 | next: () => { | 93 | next: () => { |
92 | this.notifier.success($localize`User ${this.user.username} updated.`) | 94 | this.notifier.success($localize`User ${this.user.username} updated.`) |
diff --git a/client/src/app/+admin/overview/users/user-list/user-list.component.ts b/client/src/app/+admin/overview/users/user-list/user-list.component.ts index 9fba11cbd..9a9d0f5c6 100644 --- a/client/src/app/+admin/overview/users/user-list/user-list.component.ts +++ b/client/src/app/+admin/overview/users/user-list/user-list.component.ts | |||
@@ -1,10 +1,11 @@ | |||
1 | import { SortMeta } from 'primeng/api' | 1 | import { SortMeta } from 'primeng/api' |
2 | import { Component, OnInit, ViewChild } from '@angular/core' | 2 | import { Component, OnInit, ViewChild } from '@angular/core' |
3 | import { ActivatedRoute, Router } from '@angular/router' | 3 | import { ActivatedRoute, Router } from '@angular/router' |
4 | import { AuthService, ConfirmService, Notifier, RestPagination, RestTable, ServerService, UserService } from '@app/core' | 4 | import { AuthService, ConfirmService, Notifier, RestPagination, RestTable, ServerService } from '@app/core' |
5 | import { AdvancedInputFilter } from '@app/shared/shared-forms' | 5 | import { AdvancedInputFilter } from '@app/shared/shared-forms' |
6 | import { DropdownAction } from '@app/shared/shared-main' | 6 | import { DropdownAction } from '@app/shared/shared-main' |
7 | import { UserBanModalComponent } from '@app/shared/shared-moderation' | 7 | import { UserBanModalComponent } from '@app/shared/shared-moderation' |
8 | import { UserAdminService } from '@app/shared/shared-users' | ||
8 | import { User, UserRole } from '@shared/models' | 9 | import { User, UserRole } from '@shared/models' |
9 | 10 | ||
10 | type UserForList = User & { | 11 | type UserForList = User & { |
@@ -57,7 +58,7 @@ export class UserListComponent extends RestTable implements OnInit { | |||
57 | private confirmService: ConfirmService, | 58 | private confirmService: ConfirmService, |
58 | private serverService: ServerService, | 59 | private serverService: ServerService, |
59 | private auth: AuthService, | 60 | private auth: AuthService, |
60 | private userService: UserService | 61 | private userAdminService: UserAdminService |
61 | ) { | 62 | ) { |
62 | super() | 63 | super() |
63 | } | 64 | } |
@@ -177,7 +178,7 @@ export class UserListComponent extends RestTable implements OnInit { | |||
177 | const res = await this.confirmService.confirm($localize`Do you really want to unban ${users.length} users?`, $localize`Unban`) | 178 | const res = await this.confirmService.confirm($localize`Do you really want to unban ${users.length} users?`, $localize`Unban`) |
178 | if (res === false) return | 179 | if (res === false) return |
179 | 180 | ||
180 | this.userService.unbanUsers(users) | 181 | this.userAdminService.unbanUsers(users) |
181 | .subscribe({ | 182 | .subscribe({ |
182 | next: () => { | 183 | next: () => { |
183 | this.notifier.success($localize`${users.length} users unbanned.`) | 184 | this.notifier.success($localize`${users.length} users unbanned.`) |
@@ -200,7 +201,7 @@ export class UserListComponent extends RestTable implements OnInit { | |||
200 | const res = await this.confirmService.confirm(message, $localize`Delete`) | 201 | const res = await this.confirmService.confirm(message, $localize`Delete`) |
201 | if (res === false) return | 202 | if (res === false) return |
202 | 203 | ||
203 | this.userService.removeUser(users) | 204 | this.userAdminService.removeUser(users) |
204 | .subscribe({ | 205 | .subscribe({ |
205 | next: () => { | 206 | next: () => { |
206 | this.notifier.success($localize`${users.length} users deleted.`) | 207 | this.notifier.success($localize`${users.length} users deleted.`) |
@@ -212,7 +213,7 @@ export class UserListComponent extends RestTable implements OnInit { | |||
212 | } | 213 | } |
213 | 214 | ||
214 | setEmailsAsVerified (users: User[]) { | 215 | setEmailsAsVerified (users: User[]) { |
215 | this.userService.updateUsers(users, { emailVerified: true }) | 216 | this.userAdminService.updateUsers(users, { emailVerified: true }) |
216 | .subscribe({ | 217 | .subscribe({ |
217 | next: () => { | 218 | next: () => { |
218 | this.notifier.success($localize`${users.length} users email set as verified.`) | 219 | this.notifier.success($localize`${users.length} users email set as verified.`) |
@@ -230,7 +231,7 @@ export class UserListComponent extends RestTable implements OnInit { | |||
230 | protected reloadData () { | 231 | protected reloadData () { |
231 | this.selectedUsers = [] | 232 | this.selectedUsers = [] |
232 | 233 | ||
233 | this.userService.getUsers({ | 234 | this.userAdminService.getUsers({ |
234 | pagination: this.pagination, | 235 | pagination: this.pagination, |
235 | sort: this.sort, | 236 | sort: this.sort, |
236 | search: this.search | 237 | search: this.search |
diff --git a/client/src/app/+signup/+register/register-step-channel.component.ts b/client/src/app/+signup/+register/register-step-channel.component.ts index d965a7865..1bc0ccfd3 100644 --- a/client/src/app/+signup/+register/register-step-channel.component.ts +++ b/client/src/app/+signup/+register/register-step-channel.component.ts | |||
@@ -2,9 +2,9 @@ import { concat, of } from 'rxjs' | |||
2 | import { pairwise } from 'rxjs/operators' | 2 | import { pairwise } from 'rxjs/operators' |
3 | import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core' | 3 | import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core' |
4 | import { FormGroup } from '@angular/forms' | 4 | import { FormGroup } from '@angular/forms' |
5 | import { UserService } from '@app/core' | ||
6 | import { VIDEO_CHANNEL_DISPLAY_NAME_VALIDATOR, VIDEO_CHANNEL_NAME_VALIDATOR } from '@app/shared/form-validators/video-channel-validators' | 5 | import { VIDEO_CHANNEL_DISPLAY_NAME_VALIDATOR, VIDEO_CHANNEL_NAME_VALIDATOR } from '@app/shared/form-validators/video-channel-validators' |
7 | import { FormReactive, FormValidatorService } from '@app/shared/shared-forms' | 6 | import { FormReactive, FormValidatorService } from '@app/shared/shared-forms' |
7 | import { UserSignupService } from '@app/shared/shared-users' | ||
8 | 8 | ||
9 | @Component({ | 9 | @Component({ |
10 | selector: 'my-register-step-channel', | 10 | selector: 'my-register-step-channel', |
@@ -17,7 +17,7 @@ export class RegisterStepChannelComponent extends FormReactive implements OnInit | |||
17 | 17 | ||
18 | constructor ( | 18 | constructor ( |
19 | protected formValidatorService: FormValidatorService, | 19 | protected formValidatorService: FormValidatorService, |
20 | private userService: UserService | 20 | private userSignupService: UserSignupService |
21 | ) { | 21 | ) { |
22 | super() | 22 | super() |
23 | } | 23 | } |
@@ -48,7 +48,7 @@ export class RegisterStepChannelComponent extends FormReactive implements OnInit | |||
48 | private onDisplayNameChange (oldDisplayName: string, newDisplayName: string) { | 48 | private onDisplayNameChange (oldDisplayName: string, newDisplayName: string) { |
49 | const name = this.form.value['name'] || '' | 49 | const name = this.form.value['name'] || '' |
50 | 50 | ||
51 | const newName = this.userService.getNewUsername(oldDisplayName, newDisplayName, name) | 51 | const newName = this.userSignupService.getNewUsername(oldDisplayName, newDisplayName, name) |
52 | this.form.patchValue({ name: newName }) | 52 | this.form.patchValue({ name: newName }) |
53 | } | 53 | } |
54 | } | 54 | } |
diff --git a/client/src/app/+signup/+register/register-step-user.component.ts b/client/src/app/+signup/+register/register-step-user.component.ts index 716cd8c78..92ddfca2e 100644 --- a/client/src/app/+signup/+register/register-step-user.component.ts +++ b/client/src/app/+signup/+register/register-step-user.component.ts | |||
@@ -2,7 +2,6 @@ import { concat, of } from 'rxjs' | |||
2 | import { pairwise } from 'rxjs/operators' | 2 | import { pairwise } from 'rxjs/operators' |
3 | import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core' | 3 | import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core' |
4 | import { FormGroup } from '@angular/forms' | 4 | import { FormGroup } from '@angular/forms' |
5 | import { UserService } from '@app/core' | ||
6 | import { | 5 | import { |
7 | USER_DISPLAY_NAME_REQUIRED_VALIDATOR, | 6 | USER_DISPLAY_NAME_REQUIRED_VALIDATOR, |
8 | USER_EMAIL_VALIDATOR, | 7 | USER_EMAIL_VALIDATOR, |
@@ -10,6 +9,7 @@ import { | |||
10 | USER_USERNAME_VALIDATOR | 9 | USER_USERNAME_VALIDATOR |
11 | } from '@app/shared/form-validators/user-validators' | 10 | } from '@app/shared/form-validators/user-validators' |
12 | import { FormReactive, FormValidatorService } from '@app/shared/shared-forms' | 11 | import { FormReactive, FormValidatorService } from '@app/shared/shared-forms' |
12 | import { UserSignupService } from '@app/shared/shared-users' | ||
13 | 13 | ||
14 | @Component({ | 14 | @Component({ |
15 | selector: 'my-register-step-user', | 15 | selector: 'my-register-step-user', |
@@ -23,7 +23,7 @@ export class RegisterStepUserComponent extends FormReactive implements OnInit { | |||
23 | 23 | ||
24 | constructor ( | 24 | constructor ( |
25 | protected formValidatorService: FormValidatorService, | 25 | protected formValidatorService: FormValidatorService, |
26 | private userService: UserService | 26 | private userSignupService: UserSignupService |
27 | ) { | 27 | ) { |
28 | super() | 28 | super() |
29 | } | 29 | } |
@@ -52,7 +52,7 @@ export class RegisterStepUserComponent extends FormReactive implements OnInit { | |||
52 | private onDisplayNameChange (oldDisplayName: string, newDisplayName: string) { | 52 | private onDisplayNameChange (oldDisplayName: string, newDisplayName: string) { |
53 | const username = this.form.value['username'] || '' | 53 | const username = this.form.value['username'] || '' |
54 | 54 | ||
55 | const newUsername = this.userService.getNewUsername(oldDisplayName, newDisplayName, username) | 55 | const newUsername = this.userSignupService.getNewUsername(oldDisplayName, newDisplayName, username) |
56 | this.form.patchValue({ username: newUsername }) | 56 | this.form.patchValue({ username: newUsername }) |
57 | } | 57 | } |
58 | } | 58 | } |
diff --git a/client/src/app/+signup/+register/register.component.ts b/client/src/app/+signup/+register/register.component.ts index bb7276459..b4a7c0d0e 100644 --- a/client/src/app/+signup/+register/register.component.ts +++ b/client/src/app/+signup/+register/register.component.ts | |||
@@ -1,12 +1,13 @@ | |||
1 | import { Component, OnInit } from '@angular/core' | 1 | import { Component, OnInit } from '@angular/core' |
2 | import { FormGroup } from '@angular/forms' | 2 | import { FormGroup } from '@angular/forms' |
3 | import { ActivatedRoute } from '@angular/router' | 3 | import { ActivatedRoute } from '@angular/router' |
4 | import { AuthService, UserService } from '@app/core' | 4 | import { AuthService } from '@app/core' |
5 | import { HooksService } from '@app/core/plugins/hooks.service' | 5 | import { HooksService } from '@app/core/plugins/hooks.service' |
6 | import { InstanceAboutAccordionComponent } from '@app/shared/shared-instance' | ||
7 | import { UserSignupService } from '@app/shared/shared-users' | ||
6 | import { NgbAccordion } from '@ng-bootstrap/ng-bootstrap' | 8 | import { NgbAccordion } from '@ng-bootstrap/ng-bootstrap' |
7 | import { UserRegister } from '@shared/models' | 9 | import { UserRegister } from '@shared/models' |
8 | import { ServerConfig } from '@shared/models/server' | 10 | import { ServerConfig } from '@shared/models/server' |
9 | import { InstanceAboutAccordionComponent } from '@app/shared/shared-instance' | ||
10 | 11 | ||
11 | @Component({ | 12 | @Component({ |
12 | selector: 'my-register', | 13 | selector: 'my-register', |
@@ -49,7 +50,7 @@ export class RegisterComponent implements OnInit { | |||
49 | constructor ( | 50 | constructor ( |
50 | private route: ActivatedRoute, | 51 | private route: ActivatedRoute, |
51 | private authService: AuthService, | 52 | private authService: AuthService, |
52 | private userService: UserService, | 53 | private userSignupService: UserSignupService, |
53 | private hooks: HooksService | 54 | private hooks: HooksService |
54 | ) { } | 55 | ) { } |
55 | 56 | ||
@@ -128,7 +129,7 @@ export class RegisterComponent implements OnInit { | |||
128 | 'filter:api.signup.registration.create.params' | 129 | 'filter:api.signup.registration.create.params' |
129 | ) | 130 | ) |
130 | 131 | ||
131 | this.userService.signup(body).subscribe({ | 132 | this.userSignupService.signup(body).subscribe({ |
132 | next: () => { | 133 | next: () => { |
133 | this.signupDone = true | 134 | this.signupDone = true |
134 | 135 | ||
diff --git a/client/src/app/+signup/+register/register.module.ts b/client/src/app/+signup/+register/register.module.ts index c36da53d5..52cdb33bc 100644 --- a/client/src/app/+signup/+register/register.module.ts +++ b/client/src/app/+signup/+register/register.module.ts | |||
@@ -1,6 +1,6 @@ | |||
1 | import { CdkStepperModule } from '@angular/cdk/stepper' | 1 | import { CdkStepperModule } from '@angular/cdk/stepper' |
2 | import { NgModule } from '@angular/core' | 2 | import { NgModule } from '@angular/core' |
3 | import { SignupSharedModule } from '@app/+signup/shared/signup-shared.module' | 3 | import { SharedSignupModule } from '@app/+signup/shared/shared-signup.module' |
4 | import { SharedInstanceModule } from '@app/shared/shared-instance' | 4 | import { SharedInstanceModule } from '@app/shared/shared-instance' |
5 | import { CustomStepperComponent } from './custom-stepper.component' | 5 | import { CustomStepperComponent } from './custom-stepper.component' |
6 | import { RegisterRoutingModule } from './register-routing.module' | 6 | import { RegisterRoutingModule } from './register-routing.module' |
@@ -15,7 +15,7 @@ import { RegisterComponent } from './register.component' | |||
15 | 15 | ||
16 | CdkStepperModule, | 16 | CdkStepperModule, |
17 | 17 | ||
18 | SignupSharedModule, | 18 | SharedSignupModule, |
19 | 19 | ||
20 | SharedInstanceModule | 20 | SharedInstanceModule |
21 | ], | 21 | ], |
diff --git a/client/src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.ts b/client/src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.ts index 83c24a251..a0ed66a3a 100644 --- a/client/src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.ts +++ b/client/src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.ts | |||
@@ -1,7 +1,8 @@ | |||
1 | import { Component, OnInit } from '@angular/core' | 1 | import { Component, OnInit } from '@angular/core' |
2 | import { Notifier, RedirectService, ServerService, UserService } from '@app/core' | 2 | import { Notifier, RedirectService, ServerService } from '@app/core' |
3 | import { USER_EMAIL_VALIDATOR } from '@app/shared/form-validators/user-validators' | 3 | import { USER_EMAIL_VALIDATOR } from '@app/shared/form-validators/user-validators' |
4 | import { FormReactive, FormValidatorService } from '@app/shared/shared-forms' | 4 | import { FormReactive, FormValidatorService } from '@app/shared/shared-forms' |
5 | import { UserSignupService } from '@app/shared/shared-users' | ||
5 | 6 | ||
6 | @Component({ | 7 | @Component({ |
7 | selector: 'my-verify-account-ask-send-email', | 8 | selector: 'my-verify-account-ask-send-email', |
@@ -14,7 +15,7 @@ export class VerifyAccountAskSendEmailComponent extends FormReactive implements | |||
14 | 15 | ||
15 | constructor ( | 16 | constructor ( |
16 | protected formValidatorService: FormValidatorService, | 17 | protected formValidatorService: FormValidatorService, |
17 | private userService: UserService, | 18 | private userSignupService: UserSignupService, |
18 | private serverService: ServerService, | 19 | private serverService: ServerService, |
19 | private notifier: Notifier, | 20 | private notifier: Notifier, |
20 | private redirectService: RedirectService | 21 | private redirectService: RedirectService |
@@ -33,7 +34,7 @@ export class VerifyAccountAskSendEmailComponent extends FormReactive implements | |||
33 | 34 | ||
34 | askSendVerifyEmail () { | 35 | askSendVerifyEmail () { |
35 | const email = this.form.value['verify-email-email'] | 36 | const email = this.form.value['verify-email-email'] |
36 | this.userService.askSendVerifyEmail(email) | 37 | this.userSignupService.askSendVerifyEmail(email) |
37 | .subscribe({ | 38 | .subscribe({ |
38 | next: () => { | 39 | next: () => { |
39 | this.notifier.success($localize`An email with verification link will be sent to ${email}.`) | 40 | this.notifier.success($localize`An email with verification link will be sent to ${email}.`) |
diff --git a/client/src/app/+signup/+verify-account/verify-account-email/verify-account-email.component.ts b/client/src/app/+signup/+verify-account/verify-account-email/verify-account-email.component.ts index 827ec7652..88efce4a1 100644 --- a/client/src/app/+signup/+verify-account/verify-account-email/verify-account-email.component.ts +++ b/client/src/app/+signup/+verify-account/verify-account-email/verify-account-email.component.ts | |||
@@ -1,6 +1,7 @@ | |||
1 | import { Component, OnInit } from '@angular/core' | 1 | import { Component, OnInit } from '@angular/core' |
2 | import { ActivatedRoute } from '@angular/router' | 2 | import { ActivatedRoute } from '@angular/router' |
3 | import { AuthService, Notifier, UserService } from '@app/core' | 3 | import { AuthService, Notifier } from '@app/core' |
4 | import { UserSignupService } from '@app/shared/shared-users' | ||
4 | 5 | ||
5 | @Component({ | 6 | @Component({ |
6 | selector: 'my-verify-account-email', | 7 | selector: 'my-verify-account-email', |
@@ -16,7 +17,7 @@ export class VerifyAccountEmailComponent implements OnInit { | |||
16 | private verificationString: string | 17 | private verificationString: string |
17 | 18 | ||
18 | constructor ( | 19 | constructor ( |
19 | private userService: UserService, | 20 | private userSignupService: UserSignupService, |
20 | private authService: AuthService, | 21 | private authService: AuthService, |
21 | private notifier: Notifier, | 22 | private notifier: Notifier, |
22 | private route: ActivatedRoute | 23 | private route: ActivatedRoute |
@@ -37,7 +38,7 @@ export class VerifyAccountEmailComponent implements OnInit { | |||
37 | } | 38 | } |
38 | 39 | ||
39 | verifyEmail () { | 40 | verifyEmail () { |
40 | this.userService.verifyEmail(this.userId, this.verificationString, this.isPendingEmail) | 41 | this.userSignupService.verifyEmail(this.userId, this.verificationString, this.isPendingEmail) |
41 | .subscribe({ | 42 | .subscribe({ |
42 | next: () => { | 43 | next: () => { |
43 | if (this.authService.isLoggedIn()) { | 44 | if (this.authService.isLoggedIn()) { |
diff --git a/client/src/app/+signup/+verify-account/verify-account.module.ts b/client/src/app/+signup/+verify-account/verify-account.module.ts index 7255605d4..ec342df8d 100644 --- a/client/src/app/+signup/+verify-account/verify-account.module.ts +++ b/client/src/app/+signup/+verify-account/verify-account.module.ts | |||
@@ -1,5 +1,5 @@ | |||
1 | import { NgModule } from '@angular/core' | 1 | import { NgModule } from '@angular/core' |
2 | import { SignupSharedModule } from '../shared/signup-shared.module' | 2 | import { SharedSignupModule } from '../shared/shared-signup.module' |
3 | import { VerifyAccountAskSendEmailComponent } from './verify-account-ask-send-email/verify-account-ask-send-email.component' | 3 | import { VerifyAccountAskSendEmailComponent } from './verify-account-ask-send-email/verify-account-ask-send-email.component' |
4 | import { VerifyAccountEmailComponent } from './verify-account-email/verify-account-email.component' | 4 | import { VerifyAccountEmailComponent } from './verify-account-email/verify-account-email.component' |
5 | import { VerifyAccountRoutingModule } from './verify-account-routing.module' | 5 | import { VerifyAccountRoutingModule } from './verify-account-routing.module' |
@@ -8,7 +8,7 @@ import { VerifyAccountRoutingModule } from './verify-account-routing.module' | |||
8 | imports: [ | 8 | imports: [ |
9 | VerifyAccountRoutingModule, | 9 | VerifyAccountRoutingModule, |
10 | 10 | ||
11 | SignupSharedModule | 11 | SharedSignupModule |
12 | ], | 12 | ], |
13 | 13 | ||
14 | declarations: [ | 14 | declarations: [ |
diff --git a/client/src/app/+signup/shared/signup-shared.module.ts b/client/src/app/+signup/shared/shared-signup.module.ts index 56b0b3bae..f8b224c71 100644 --- a/client/src/app/+signup/shared/signup-shared.module.ts +++ b/client/src/app/+signup/shared/shared-signup.module.ts | |||
@@ -1,14 +1,16 @@ | |||
1 | import { NgModule } from '@angular/core' | 1 | import { NgModule } from '@angular/core' |
2 | import { SharedMainModule } from '@app/shared/shared-main' | ||
3 | import { SignupSuccessComponent } from './signup-success.component' | ||
4 | import { SharedFormModule } from '@app/shared/shared-forms' | 2 | import { SharedFormModule } from '@app/shared/shared-forms' |
5 | import { SharedGlobalIconModule } from '@app/shared/shared-icons' | 3 | import { SharedGlobalIconModule } from '@app/shared/shared-icons' |
4 | import { SharedMainModule } from '@app/shared/shared-main' | ||
5 | import { SharedUsersModule } from '@app/shared/shared-users' | ||
6 | import { SignupSuccessComponent } from './signup-success.component' | ||
6 | 7 | ||
7 | @NgModule({ | 8 | @NgModule({ |
8 | imports: [ | 9 | imports: [ |
9 | SharedMainModule, | 10 | SharedMainModule, |
10 | SharedFormModule, | 11 | SharedFormModule, |
11 | SharedGlobalIconModule | 12 | SharedGlobalIconModule, |
13 | SharedUsersModule | ||
12 | ], | 14 | ], |
13 | 15 | ||
14 | declarations: [ | 16 | declarations: [ |
@@ -26,4 +28,4 @@ import { SharedGlobalIconModule } from '@app/shared/shared-icons' | |||
26 | providers: [ | 28 | providers: [ |
27 | ] | 29 | ] |
28 | }) | 30 | }) |
29 | export class SignupSharedModule { } | 31 | export class SharedSignupModule { } |
diff --git a/client/src/app/core/users/user.service.ts b/client/src/app/core/users/user.service.ts index f173c356a..b14bff193 100644 --- a/client/src/app/core/users/user.service.ts +++ b/client/src/app/core/users/user.service.ts | |||
@@ -1,24 +1,12 @@ | |||
1 | import { SortMeta } from 'primeng/api' | 1 | import { Observable, of } from 'rxjs' |
2 | import { from, Observable, of } from 'rxjs' | 2 | import { catchError, first, map, shareReplay } from 'rxjs/operators' |
3 | import { catchError, concatMap, first, map, shareReplay, tap, toArray } from 'rxjs/operators' | ||
4 | import { HttpClient, HttpParams } from '@angular/common/http' | 3 | import { HttpClient, HttpParams } from '@angular/common/http' |
5 | import { Injectable } from '@angular/core' | 4 | import { Injectable } from '@angular/core' |
6 | import { AuthService } from '@app/core/auth' | 5 | import { AuthService } from '@app/core/auth' |
7 | import { getBytes } from '@root-helpers/bytes' | 6 | import { ActorImage, User as UserServerModel, UserUpdateMe, UserVideoQuota } from '@shared/models' |
8 | import { | ||
9 | ActorImage, | ||
10 | ResultList, | ||
11 | User as UserServerModel, | ||
12 | UserCreate, | ||
13 | UserRegister, | ||
14 | UserRole, | ||
15 | UserUpdate, | ||
16 | UserUpdateMe, | ||
17 | UserVideoQuota | ||
18 | } from '@shared/models' | ||
19 | import { environment } from '../../../environments/environment' | 7 | import { environment } from '../../../environments/environment' |
20 | import { RestExtractor, RestPagination, RestService } from '../rest' | 8 | import { RestExtractor } from '../rest' |
21 | import { UserLocalStorageService } from './' | 9 | import { UserLocalStorageService } from './user-local-storage.service' |
22 | import { User } from './user.model' | 10 | import { User } from './user.model' |
23 | 11 | ||
24 | @Injectable() | 12 | @Injectable() |
@@ -26,45 +14,44 @@ export class UserService { | |||
26 | static BASE_USERS_URL = environment.apiUrl + '/api/v1/users/' | 14 | static BASE_USERS_URL = environment.apiUrl + '/api/v1/users/' |
27 | 15 | ||
28 | private userCache: { [ id: number ]: Observable<UserServerModel> } = {} | 16 | private userCache: { [ id: number ]: Observable<UserServerModel> } = {} |
29 | |||
30 | private signupInThisSession = false | 17 | private signupInThisSession = false |
31 | 18 | ||
32 | constructor ( | 19 | constructor ( |
33 | private authHttp: HttpClient, | 20 | private authHttp: HttpClient, |
34 | private authService: AuthService, | 21 | private authService: AuthService, |
35 | private restExtractor: RestExtractor, | 22 | private restExtractor: RestExtractor, |
36 | private restService: RestService, | ||
37 | private userLocalStorageService: UserLocalStorageService | 23 | private userLocalStorageService: UserLocalStorageService |
38 | ) { } | 24 | ) { } |
39 | 25 | ||
40 | hasSignupInThisSession () { | 26 | // --------------------------------------------------------------------------- |
41 | return this.signupInThisSession | ||
42 | } | ||
43 | 27 | ||
44 | changePassword (currentPassword: string, newPassword: string) { | 28 | getUserWithCache (userId: number) { |
45 | const url = UserService.BASE_USERS_URL + 'me' | 29 | if (!this.userCache[userId]) { |
46 | const body: UserUpdateMe = { | 30 | this.userCache[userId] = this.getUser(userId).pipe(shareReplay()) |
47 | currentPassword, | ||
48 | password: newPassword | ||
49 | } | 31 | } |
50 | 32 | ||
51 | return this.authHttp.put(url, body) | 33 | return this.userCache[userId] |
52 | .pipe(catchError(err => this.restExtractor.handleError(err))) | ||
53 | } | 34 | } |
54 | 35 | ||
55 | changeEmail (password: string, newEmail: string) { | 36 | getUser (userId: number, withStats = false) { |
56 | const url = UserService.BASE_USERS_URL + 'me' | 37 | const params = new HttpParams().append('withStats', withStats + '') |
57 | const body: UserUpdateMe = { | ||
58 | currentPassword: password, | ||
59 | email: newEmail | ||
60 | } | ||
61 | 38 | ||
62 | return this.authHttp.put(url, body) | 39 | return this.authHttp.get<UserServerModel>(UserService.BASE_USERS_URL + userId, { params }) |
63 | .pipe(catchError(err => this.restExtractor.handleError(err))) | 40 | .pipe(catchError(err => this.restExtractor.handleError(err))) |
64 | } | 41 | } |
65 | 42 | ||
66 | // --------------------------------------------------------------------------- | 43 | // --------------------------------------------------------------------------- |
67 | 44 | ||
45 | setSignupInThisSession (value: boolean) { | ||
46 | this.signupInThisSession = value | ||
47 | } | ||
48 | |||
49 | hasSignupInThisSession () { | ||
50 | return this.signupInThisSession | ||
51 | } | ||
52 | |||
53 | // --------------------------------------------------------------------------- | ||
54 | |||
68 | updateMyAnonymousProfile (profile: UserUpdateMe) { | 55 | updateMyAnonymousProfile (profile: UserUpdateMe) { |
69 | this.userLocalStorageService.setUserInfo(profile) | 56 | this.userLocalStorageService.setUserInfo(profile) |
70 | } | 57 | } |
@@ -78,8 +65,42 @@ export class UserService { | |||
78 | return new User(this.userLocalStorageService.getUserInfo()) | 65 | return new User(this.userLocalStorageService.getUserInfo()) |
79 | } | 66 | } |
80 | 67 | ||
68 | getAnonymousOrLoggedUser () { | ||
69 | if (!this.authService.isLoggedIn()) { | ||
70 | return of(this.getAnonymousUser()) | ||
71 | } | ||
72 | |||
73 | return this.authService.userInformationLoaded | ||
74 | .pipe( | ||
75 | first(), | ||
76 | map(() => this.authService.getUser()) | ||
77 | ) | ||
78 | } | ||
79 | |||
81 | // --------------------------------------------------------------------------- | 80 | // --------------------------------------------------------------------------- |
82 | 81 | ||
82 | changePassword (currentPassword: string, newPassword: string) { | ||
83 | const url = UserService.BASE_USERS_URL + 'me' | ||
84 | const body: UserUpdateMe = { | ||
85 | currentPassword, | ||
86 | password: newPassword | ||
87 | } | ||
88 | |||
89 | return this.authHttp.put(url, body) | ||
90 | .pipe(catchError(err => this.restExtractor.handleError(err))) | ||
91 | } | ||
92 | |||
93 | changeEmail (password: string, newEmail: string) { | ||
94 | const url = UserService.BASE_USERS_URL + 'me' | ||
95 | const body: UserUpdateMe = { | ||
96 | currentPassword: password, | ||
97 | email: newEmail | ||
98 | } | ||
99 | |||
100 | return this.authHttp.put(url, body) | ||
101 | .pipe(catchError(err => this.restExtractor.handleError(err))) | ||
102 | } | ||
103 | |||
83 | updateMyProfile (profile: UserUpdateMe) { | 104 | updateMyProfile (profile: UserUpdateMe) { |
84 | const url = UserService.BASE_USERS_URL + 'me' | 105 | const url = UserService.BASE_USERS_URL + 'me' |
85 | 106 | ||
@@ -108,14 +129,6 @@ export class UserService { | |||
108 | .pipe(catchError(err => this.restExtractor.handleError(err))) | 129 | .pipe(catchError(err => this.restExtractor.handleError(err))) |
109 | } | 130 | } |
110 | 131 | ||
111 | signup (userCreate: UserRegister) { | ||
112 | return this.authHttp.post(UserService.BASE_USERS_URL + 'register', userCreate) | ||
113 | .pipe( | ||
114 | tap(() => this.signupInThisSession = true), | ||
115 | catchError(err => this.restExtractor.handleError(err)) | ||
116 | ) | ||
117 | } | ||
118 | |||
119 | getMyVideoQuotaUsed () { | 132 | getMyVideoQuotaUsed () { |
120 | const url = UserService.BASE_USERS_URL + 'me/video-quota-used' | 133 | const url = UserService.BASE_USERS_URL + 'me/video-quota-used' |
121 | 134 | ||
@@ -141,24 +154,6 @@ export class UserService { | |||
141 | .pipe(catchError(res => this.restExtractor.handleError(res))) | 154 | .pipe(catchError(res => this.restExtractor.handleError(res))) |
142 | } | 155 | } |
143 | 156 | ||
144 | verifyEmail (userId: number, verificationString: string, isPendingEmail: boolean) { | ||
145 | const url = `${UserService.BASE_USERS_URL}/${userId}/verify-email` | ||
146 | const body = { | ||
147 | verificationString, | ||
148 | isPendingEmail | ||
149 | } | ||
150 | |||
151 | return this.authHttp.post(url, body) | ||
152 | .pipe(catchError(res => this.restExtractor.handleError(res))) | ||
153 | } | ||
154 | |||
155 | askSendVerifyEmail (email: string) { | ||
156 | const url = UserService.BASE_USERS_URL + '/ask-send-verify-email' | ||
157 | |||
158 | return this.authHttp.post(url, { email }) | ||
159 | .pipe(catchError(err => this.restExtractor.handleError(err))) | ||
160 | } | ||
161 | |||
162 | autocomplete (search: string): Observable<string[]> { | 157 | autocomplete (search: string): Observable<string[]> { |
163 | const url = UserService.BASE_USERS_URL + 'autocomplete' | 158 | const url = UserService.BASE_USERS_URL + 'autocomplete' |
164 | const params = new HttpParams().append('search', search) | 159 | const params = new HttpParams().append('search', search) |
@@ -167,169 +162,4 @@ export class UserService { | |||
167 | .get<string[]>(url, { params }) | 162 | .get<string[]>(url, { params }) |
168 | .pipe(catchError(res => this.restExtractor.handleError(res))) | 163 | .pipe(catchError(res => this.restExtractor.handleError(res))) |
169 | } | 164 | } |
170 | |||
171 | getNewUsername (oldDisplayName: string, newDisplayName: string, currentUsername: string) { | ||
172 | // Don't update display name, the user seems to have changed it | ||
173 | if (this.displayNameToUsername(oldDisplayName) !== currentUsername) return currentUsername | ||
174 | |||
175 | return this.displayNameToUsername(newDisplayName) | ||
176 | } | ||
177 | |||
178 | displayNameToUsername (displayName: string) { | ||
179 | if (!displayName) return '' | ||
180 | |||
181 | return displayName | ||
182 | .toLowerCase() | ||
183 | .replace(/\s/g, '_') | ||
184 | .replace(/[^a-z0-9_.]/g, '') | ||
185 | } | ||
186 | |||
187 | /* ###### Admin methods ###### */ | ||
188 | |||
189 | addUser (userCreate: UserCreate) { | ||
190 | return this.authHttp.post(UserService.BASE_USERS_URL, userCreate) | ||
191 | .pipe(catchError(err => this.restExtractor.handleError(err))) | ||
192 | } | ||
193 | |||
194 | updateUser (userId: number, userUpdate: UserUpdate) { | ||
195 | return this.authHttp.put(UserService.BASE_USERS_URL + userId, userUpdate) | ||
196 | .pipe(catchError(err => this.restExtractor.handleError(err))) | ||
197 | } | ||
198 | |||
199 | updateUsers (users: UserServerModel[], userUpdate: UserUpdate) { | ||
200 | return from(users) | ||
201 | .pipe( | ||
202 | concatMap(u => this.authHttp.put(UserService.BASE_USERS_URL + u.id, userUpdate)), | ||
203 | toArray(), | ||
204 | catchError(err => this.restExtractor.handleError(err)) | ||
205 | ) | ||
206 | } | ||
207 | |||
208 | getUserWithCache (userId: number) { | ||
209 | if (!this.userCache[userId]) { | ||
210 | this.userCache[userId] = this.getUser(userId).pipe(shareReplay()) | ||
211 | } | ||
212 | |||
213 | return this.userCache[userId] | ||
214 | } | ||
215 | |||
216 | getUser (userId: number, withStats = false) { | ||
217 | const params = new HttpParams().append('withStats', withStats + '') | ||
218 | return this.authHttp.get<UserServerModel>(UserService.BASE_USERS_URL + userId, { params }) | ||
219 | .pipe(catchError(err => this.restExtractor.handleError(err))) | ||
220 | } | ||
221 | |||
222 | getUsers (parameters: { | ||
223 | pagination: RestPagination | ||
224 | sort: SortMeta | ||
225 | search?: string | ||
226 | }): Observable<ResultList<UserServerModel>> { | ||
227 | const { pagination, sort, search } = parameters | ||
228 | |||
229 | let params = new HttpParams() | ||
230 | params = this.restService.addRestGetParams(params, pagination, sort) | ||
231 | |||
232 | if (search) { | ||
233 | const filters = this.restService.parseQueryStringFilter(search, { | ||
234 | blocked: { | ||
235 | prefix: 'banned:', | ||
236 | isBoolean: true | ||
237 | } | ||
238 | }) | ||
239 | |||
240 | params = this.restService.addObjectParams(params, filters) | ||
241 | } | ||
242 | |||
243 | return this.authHttp.get<ResultList<UserServerModel>>(UserService.BASE_USERS_URL, { params }) | ||
244 | .pipe( | ||
245 | map(res => this.restExtractor.convertResultListDateToHuman(res)), | ||
246 | map(res => this.restExtractor.applyToResultListData(res, this.formatUser.bind(this))), | ||
247 | catchError(err => this.restExtractor.handleError(err)) | ||
248 | ) | ||
249 | } | ||
250 | |||
251 | removeUser (usersArg: UserServerModel | UserServerModel[]) { | ||
252 | const users = Array.isArray(usersArg) ? usersArg : [ usersArg ] | ||
253 | |||
254 | return from(users) | ||
255 | .pipe( | ||
256 | concatMap(u => this.authHttp.delete(UserService.BASE_USERS_URL + u.id)), | ||
257 | toArray(), | ||
258 | catchError(err => this.restExtractor.handleError(err)) | ||
259 | ) | ||
260 | } | ||
261 | |||
262 | banUsers (usersArg: UserServerModel | UserServerModel[], reason?: string) { | ||
263 | const body = reason ? { reason } : {} | ||
264 | const users = Array.isArray(usersArg) ? usersArg : [ usersArg ] | ||
265 | |||
266 | return from(users) | ||
267 | .pipe( | ||
268 | concatMap(u => this.authHttp.post(UserService.BASE_USERS_URL + u.id + '/block', body)), | ||
269 | toArray(), | ||
270 | catchError(err => this.restExtractor.handleError(err)) | ||
271 | ) | ||
272 | } | ||
273 | |||
274 | unbanUsers (usersArg: UserServerModel | UserServerModel[]) { | ||
275 | const users = Array.isArray(usersArg) ? usersArg : [ usersArg ] | ||
276 | |||
277 | return from(users) | ||
278 | .pipe( | ||
279 | concatMap(u => this.authHttp.post(UserService.BASE_USERS_URL + u.id + '/unblock', {})), | ||
280 | toArray(), | ||
281 | catchError(err => this.restExtractor.handleError(err)) | ||
282 | ) | ||
283 | } | ||
284 | |||
285 | getAnonymousOrLoggedUser () { | ||
286 | if (!this.authService.isLoggedIn()) { | ||
287 | return of(this.getAnonymousUser()) | ||
288 | } | ||
289 | |||
290 | return this.authService.userInformationLoaded | ||
291 | .pipe( | ||
292 | first(), | ||
293 | map(() => this.authService.getUser()) | ||
294 | ) | ||
295 | } | ||
296 | |||
297 | private formatUser (user: UserServerModel) { | ||
298 | let videoQuota | ||
299 | if (user.videoQuota === -1) { | ||
300 | videoQuota = '∞' | ||
301 | } else { | ||
302 | videoQuota = getBytes(user.videoQuota, 0) | ||
303 | } | ||
304 | |||
305 | const videoQuotaUsed = getBytes(user.videoQuotaUsed, 0) | ||
306 | |||
307 | let videoQuotaDaily: string | ||
308 | let videoQuotaUsedDaily: string | ||
309 | if (user.videoQuotaDaily === -1) { | ||
310 | videoQuotaDaily = '∞' | ||
311 | videoQuotaUsedDaily = getBytes(0, 0) + '' | ||
312 | } else { | ||
313 | videoQuotaDaily = getBytes(user.videoQuotaDaily, 0) + '' | ||
314 | videoQuotaUsedDaily = getBytes(user.videoQuotaUsedDaily || 0, 0) + '' | ||
315 | } | ||
316 | |||
317 | const roleLabels: { [ id in UserRole ]: string } = { | ||
318 | [UserRole.USER]: $localize`User`, | ||
319 | [UserRole.ADMINISTRATOR]: $localize`Administrator`, | ||
320 | [UserRole.MODERATOR]: $localize`Moderator` | ||
321 | } | ||
322 | |||
323 | return Object.assign(user, { | ||
324 | roleLabel: roleLabels[user.role], | ||
325 | videoQuota, | ||
326 | videoQuotaUsed, | ||
327 | rawVideoQuota: user.videoQuota, | ||
328 | rawVideoQuotaUsed: user.videoQuotaUsed, | ||
329 | videoQuotaDaily, | ||
330 | videoQuotaUsedDaily, | ||
331 | rawVideoQuotaDaily: user.videoQuotaDaily, | ||
332 | rawVideoQuotaUsedDaily: user.videoQuotaUsedDaily | ||
333 | }) | ||
334 | } | ||
335 | } | 165 | } |
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 7cadda67c..dac4ae951 100644 --- a/client/src/app/shared/shared-moderation/shared-moderation.module.ts +++ b/client/src/app/shared/shared-moderation/shared-moderation.module.ts | |||
@@ -1,10 +1,13 @@ | |||
1 | 1 | ||
2 | import { NgModule } from '@angular/core' | 2 | import { NgModule } from '@angular/core' |
3 | import { SharedActorImageModule } from '../shared-actor-image/shared-actor-image.module' | ||
3 | import { SharedFormModule } from '../shared-forms/shared-form.module' | 4 | import { SharedFormModule } from '../shared-forms/shared-form.module' |
4 | import { SharedGlobalIconModule } from '../shared-icons' | 5 | import { SharedGlobalIconModule } from '../shared-icons' |
5 | import { SharedMainModule } from '../shared-main/shared-main.module' | 6 | import { SharedMainModule } from '../shared-main/shared-main.module' |
7 | import { SharedUsersModule } from '../shared-users' | ||
6 | import { SharedVideoCommentModule } from '../shared-video-comment' | 8 | import { SharedVideoCommentModule } from '../shared-video-comment' |
7 | import { AbuseService } from './abuse.service' | 9 | import { AbuseService } from './abuse.service' |
10 | import { AccountBlockBadgesComponent } from './account-block-badges.component' | ||
8 | import { BatchDomainsModalComponent } from './batch-domains-modal.component' | 11 | import { BatchDomainsModalComponent } from './batch-domains-modal.component' |
9 | import { BlocklistService } from './blocklist.service' | 12 | import { BlocklistService } from './blocklist.service' |
10 | import { BulkService } from './bulk.service' | 13 | import { BulkService } from './bulk.service' |
@@ -13,8 +16,6 @@ import { UserBanModalComponent } from './user-ban-modal.component' | |||
13 | import { UserModerationDropdownComponent } from './user-moderation-dropdown.component' | 16 | import { UserModerationDropdownComponent } from './user-moderation-dropdown.component' |
14 | import { VideoBlockComponent } from './video-block.component' | 17 | import { VideoBlockComponent } from './video-block.component' |
15 | import { VideoBlockService } from './video-block.service' | 18 | import { VideoBlockService } from './video-block.service' |
16 | import { AccountBlockBadgesComponent } from './account-block-badges.component' | ||
17 | import { SharedActorImageModule } from '../shared-actor-image/shared-actor-image.module' | ||
18 | 19 | ||
19 | @NgModule({ | 20 | @NgModule({ |
20 | imports: [ | 21 | imports: [ |
@@ -22,7 +23,8 @@ import { SharedActorImageModule } from '../shared-actor-image/shared-actor-image | |||
22 | SharedFormModule, | 23 | SharedFormModule, |
23 | SharedGlobalIconModule, | 24 | SharedGlobalIconModule, |
24 | SharedVideoCommentModule, | 25 | SharedVideoCommentModule, |
25 | SharedActorImageModule | 26 | SharedActorImageModule, |
27 | SharedUsersModule | ||
26 | ], | 28 | ], |
27 | 29 | ||
28 | declarations: [ | 30 | declarations: [ |
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 b2ce019c5..17cad18ec 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,10 +1,11 @@ | |||
1 | import { Component, EventEmitter, OnInit, Output, ViewChild } from '@angular/core' | 1 | import { Component, EventEmitter, OnInit, Output, ViewChild } from '@angular/core' |
2 | import { Notifier, UserService } from '@app/core' | 2 | import { Notifier } from '@app/core' |
3 | import { FormReactive, FormValidatorService } from '@app/shared/shared-forms' | 3 | import { FormReactive, FormValidatorService } from '@app/shared/shared-forms' |
4 | import { NgbModal } from '@ng-bootstrap/ng-bootstrap' | 4 | import { NgbModal } from '@ng-bootstrap/ng-bootstrap' |
5 | import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap/modal/modal-ref' | 5 | import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap/modal/modal-ref' |
6 | import { User } from '@shared/models' | 6 | import { User } from '@shared/models' |
7 | import { USER_BAN_REASON_VALIDATOR } from '../form-validators/user-validators' | 7 | import { USER_BAN_REASON_VALIDATOR } from '../form-validators/user-validators' |
8 | import { UserAdminService } from '../shared-users' | ||
8 | 9 | ||
9 | @Component({ | 10 | @Component({ |
10 | selector: 'my-user-ban-modal', | 11 | selector: 'my-user-ban-modal', |
@@ -23,7 +24,7 @@ export class UserBanModalComponent extends FormReactive implements OnInit { | |||
23 | protected formValidatorService: FormValidatorService, | 24 | protected formValidatorService: FormValidatorService, |
24 | private modalService: NgbModal, | 25 | private modalService: NgbModal, |
25 | private notifier: Notifier, | 26 | private notifier: Notifier, |
26 | private userService: UserService | 27 | private userAdminService: UserAdminService |
27 | ) { | 28 | ) { |
28 | super() | 29 | super() |
29 | } | 30 | } |
@@ -50,7 +51,7 @@ export class UserBanModalComponent extends FormReactive implements OnInit { | |||
50 | banUser () { | 51 | banUser () { |
51 | const reason = this.form.value['reason'] || undefined | 52 | const reason = this.form.value['reason'] || undefined |
52 | 53 | ||
53 | this.userService.banUsers(this.usersToBan, reason) | 54 | this.userAdminService.banUsers(this.usersToBan, reason) |
54 | .subscribe({ | 55 | .subscribe({ |
55 | next: () => { | 56 | next: () => { |
56 | const message = Array.isArray(this.usersToBan) | 57 | const message = Array.isArray(this.usersToBan) |
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 e2cd2cdc1..0d19565ef 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 | |||
@@ -1,7 +1,8 @@ | |||
1 | import { Component, EventEmitter, Input, OnChanges, OnInit, Output, ViewChild } from '@angular/core' | 1 | import { Component, EventEmitter, Input, OnChanges, OnInit, Output, ViewChild } from '@angular/core' |
2 | import { AuthService, ConfirmService, Notifier, ServerService, UserService } from '@app/core' | 2 | import { AuthService, ConfirmService, Notifier, ServerService } from '@app/core' |
3 | import { Account, DropdownAction } from '@app/shared/shared-main' | 3 | import { Account, DropdownAction } from '@app/shared/shared-main' |
4 | import { BulkRemoveCommentsOfBody, User, UserRight } from '@shared/models' | 4 | import { BulkRemoveCommentsOfBody, User, UserRight } from '@shared/models' |
5 | import { UserAdminService } from '../shared-users' | ||
5 | import { BlocklistService } from './blocklist.service' | 6 | import { BlocklistService } from './blocklist.service' |
6 | import { BulkService } from './bulk.service' | 7 | import { BulkService } from './bulk.service' |
7 | import { UserBanModalComponent } from './user-ban-modal.component' | 8 | import { UserBanModalComponent } from './user-ban-modal.component' |
@@ -35,7 +36,7 @@ export class UserModerationDropdownComponent implements OnInit, OnChanges { | |||
35 | private notifier: Notifier, | 36 | private notifier: Notifier, |
36 | private confirmService: ConfirmService, | 37 | private confirmService: ConfirmService, |
37 | private serverService: ServerService, | 38 | private serverService: ServerService, |
38 | private userService: UserService, | 39 | private userAdminService: UserAdminService, |
39 | private blocklistService: BlocklistService, | 40 | private blocklistService: BlocklistService, |
40 | private bulkService: BulkService | 41 | private bulkService: BulkService |
41 | ) { } | 42 | ) { } |
@@ -66,7 +67,7 @@ export class UserModerationDropdownComponent implements OnInit, OnChanges { | |||
66 | const res = await this.confirmService.confirm($localize`Do you really want to unban ${user.username}?`, $localize`Unban`) | 67 | const res = await this.confirmService.confirm($localize`Do you really want to unban ${user.username}?`, $localize`Unban`) |
67 | if (res === false) return | 68 | if (res === false) return |
68 | 69 | ||
69 | this.userService.unbanUsers(user) | 70 | this.userAdminService.unbanUsers(user) |
70 | .subscribe({ | 71 | .subscribe({ |
71 | next: () => { | 72 | next: () => { |
72 | this.notifier.success($localize`User ${user.username} unbanned.`) | 73 | this.notifier.success($localize`User ${user.username} unbanned.`) |
@@ -87,7 +88,7 @@ export class UserModerationDropdownComponent implements OnInit, OnChanges { | |||
87 | const res = await this.confirmService.confirm(message, $localize`Delete ${user.username}`) | 88 | const res = await this.confirmService.confirm(message, $localize`Delete ${user.username}`) |
88 | if (res === false) return | 89 | if (res === false) return |
89 | 90 | ||
90 | this.userService.removeUser(user) | 91 | this.userAdminService.removeUser(user) |
91 | .subscribe({ | 92 | .subscribe({ |
92 | next: () => { | 93 | next: () => { |
93 | this.notifier.success($localize`User ${user.username} deleted.`) | 94 | this.notifier.success($localize`User ${user.username} deleted.`) |
@@ -99,7 +100,7 @@ export class UserModerationDropdownComponent implements OnInit, OnChanges { | |||
99 | } | 100 | } |
100 | 101 | ||
101 | setEmailAsVerified (user: User) { | 102 | setEmailAsVerified (user: User) { |
102 | this.userService.updateUser(user.id, { emailVerified: true }) | 103 | this.userAdminService.updateUser(user.id, { emailVerified: true }) |
103 | .subscribe({ | 104 | .subscribe({ |
104 | next: () => { | 105 | next: () => { |
105 | this.notifier.success($localize`User ${user.username} email set as verified`) | 106 | this.notifier.success($localize`User ${user.username} email set as verified`) |
diff --git a/client/src/app/shared/shared-users/index.ts b/client/src/app/shared/shared-users/index.ts new file mode 100644 index 000000000..8f90f2515 --- /dev/null +++ b/client/src/app/shared/shared-users/index.ts | |||
@@ -0,0 +1,4 @@ | |||
1 | export * from './user-admin.service' | ||
2 | export * from './user-signup.service' | ||
3 | |||
4 | export * from './shared-users.module' | ||
diff --git a/client/src/app/shared/shared-users/shared-users.module.ts b/client/src/app/shared/shared-users/shared-users.module.ts new file mode 100644 index 000000000..2a1dadf20 --- /dev/null +++ b/client/src/app/shared/shared-users/shared-users.module.ts | |||
@@ -0,0 +1,21 @@ | |||
1 | |||
2 | import { NgModule } from '@angular/core' | ||
3 | import { SharedMainModule } from '../shared-main/shared-main.module' | ||
4 | import { UserAdminService } from './user-admin.service' | ||
5 | import { UserSignupService } from './user-signup.service' | ||
6 | |||
7 | @NgModule({ | ||
8 | imports: [ | ||
9 | SharedMainModule | ||
10 | ], | ||
11 | |||
12 | declarations: [ ], | ||
13 | |||
14 | exports: [], | ||
15 | |||
16 | providers: [ | ||
17 | UserSignupService, | ||
18 | UserAdminService | ||
19 | ] | ||
20 | }) | ||
21 | export class SharedUsersModule { } | ||
diff --git a/client/src/app/shared/shared-users/user-admin.service.ts b/client/src/app/shared/shared-users/user-admin.service.ts new file mode 100644 index 000000000..3db271c4a --- /dev/null +++ b/client/src/app/shared/shared-users/user-admin.service.ts | |||
@@ -0,0 +1,139 @@ | |||
1 | import { SortMeta } from 'primeng/api' | ||
2 | import { from, Observable } from 'rxjs' | ||
3 | import { catchError, concatMap, map, toArray } from 'rxjs/operators' | ||
4 | import { HttpClient, HttpParams } from '@angular/common/http' | ||
5 | import { Injectable } from '@angular/core' | ||
6 | import { RestExtractor, RestPagination, RestService, UserService } from '@app/core' | ||
7 | import { getBytes } from '@root-helpers/bytes' | ||
8 | import { ResultList, User as UserServerModel, UserCreate, UserRole, UserUpdate } from '@shared/models' | ||
9 | |||
10 | @Injectable() | ||
11 | export class UserAdminService { | ||
12 | |||
13 | constructor ( | ||
14 | private authHttp: HttpClient, | ||
15 | private restExtractor: RestExtractor, | ||
16 | private restService: RestService | ||
17 | ) { } | ||
18 | |||
19 | addUser (userCreate: UserCreate) { | ||
20 | return this.authHttp.post(UserService.BASE_USERS_URL, userCreate) | ||
21 | .pipe(catchError(err => this.restExtractor.handleError(err))) | ||
22 | } | ||
23 | |||
24 | updateUser (userId: number, userUpdate: UserUpdate) { | ||
25 | return this.authHttp.put(UserService.BASE_USERS_URL + userId, userUpdate) | ||
26 | .pipe(catchError(err => this.restExtractor.handleError(err))) | ||
27 | } | ||
28 | |||
29 | updateUsers (users: UserServerModel[], userUpdate: UserUpdate) { | ||
30 | return from(users) | ||
31 | .pipe( | ||
32 | concatMap(u => this.authHttp.put(UserService.BASE_USERS_URL + u.id, userUpdate)), | ||
33 | toArray(), | ||
34 | catchError(err => this.restExtractor.handleError(err)) | ||
35 | ) | ||
36 | } | ||
37 | |||
38 | getUsers (parameters: { | ||
39 | pagination: RestPagination | ||
40 | sort: SortMeta | ||
41 | search?: string | ||
42 | }): Observable<ResultList<UserServerModel>> { | ||
43 | const { pagination, sort, search } = parameters | ||
44 | |||
45 | let params = new HttpParams() | ||
46 | params = this.restService.addRestGetParams(params, pagination, sort) | ||
47 | |||
48 | if (search) { | ||
49 | const filters = this.restService.parseQueryStringFilter(search, { | ||
50 | blocked: { | ||
51 | prefix: 'banned:', | ||
52 | isBoolean: true | ||
53 | } | ||
54 | }) | ||
55 | |||
56 | params = this.restService.addObjectParams(params, filters) | ||
57 | } | ||
58 | |||
59 | return this.authHttp.get<ResultList<UserServerModel>>(UserService.BASE_USERS_URL, { params }) | ||
60 | .pipe( | ||
61 | map(res => this.restExtractor.convertResultListDateToHuman(res)), | ||
62 | map(res => this.restExtractor.applyToResultListData(res, this.formatUser.bind(this))), | ||
63 | catchError(err => this.restExtractor.handleError(err)) | ||
64 | ) | ||
65 | } | ||
66 | |||
67 | removeUser (usersArg: UserServerModel | UserServerModel[]) { | ||
68 | const users = Array.isArray(usersArg) ? usersArg : [ usersArg ] | ||
69 | |||
70 | return from(users) | ||
71 | .pipe( | ||
72 | concatMap(u => this.authHttp.delete(UserService.BASE_USERS_URL + u.id)), | ||
73 | toArray(), | ||
74 | catchError(err => this.restExtractor.handleError(err)) | ||
75 | ) | ||
76 | } | ||
77 | |||
78 | banUsers (usersArg: UserServerModel | UserServerModel[], reason?: string) { | ||
79 | const body = reason ? { reason } : {} | ||
80 | const users = Array.isArray(usersArg) ? usersArg : [ usersArg ] | ||
81 | |||
82 | return from(users) | ||
83 | .pipe( | ||
84 | concatMap(u => this.authHttp.post(UserService.BASE_USERS_URL + u.id + '/block', body)), | ||
85 | toArray(), | ||
86 | catchError(err => this.restExtractor.handleError(err)) | ||
87 | ) | ||
88 | } | ||
89 | |||
90 | unbanUsers (usersArg: UserServerModel | UserServerModel[]) { | ||
91 | const users = Array.isArray(usersArg) ? usersArg : [ usersArg ] | ||
92 | |||
93 | return from(users) | ||
94 | .pipe( | ||
95 | concatMap(u => this.authHttp.post(UserService.BASE_USERS_URL + u.id + '/unblock', {})), | ||
96 | toArray(), | ||
97 | catchError(err => this.restExtractor.handleError(err)) | ||
98 | ) | ||
99 | } | ||
100 | |||
101 | private formatUser (user: UserServerModel) { | ||
102 | let videoQuota | ||
103 | if (user.videoQuota === -1) { | ||
104 | videoQuota = '∞' | ||
105 | } else { | ||
106 | videoQuota = getBytes(user.videoQuota, 0) | ||
107 | } | ||
108 | |||
109 | const videoQuotaUsed = getBytes(user.videoQuotaUsed, 0) | ||
110 | |||
111 | let videoQuotaDaily: string | ||
112 | let videoQuotaUsedDaily: string | ||
113 | if (user.videoQuotaDaily === -1) { | ||
114 | videoQuotaDaily = '∞' | ||
115 | videoQuotaUsedDaily = getBytes(0, 0) + '' | ||
116 | } else { | ||
117 | videoQuotaDaily = getBytes(user.videoQuotaDaily, 0) + '' | ||
118 | videoQuotaUsedDaily = getBytes(user.videoQuotaUsedDaily || 0, 0) + '' | ||
119 | } | ||
120 | |||
121 | const roleLabels: { [ id in UserRole ]: string } = { | ||
122 | [UserRole.USER]: $localize`User`, | ||
123 | [UserRole.ADMINISTRATOR]: $localize`Administrator`, | ||
124 | [UserRole.MODERATOR]: $localize`Moderator` | ||
125 | } | ||
126 | |||
127 | return Object.assign(user, { | ||
128 | roleLabel: roleLabels[user.role], | ||
129 | videoQuota, | ||
130 | videoQuotaUsed, | ||
131 | rawVideoQuota: user.videoQuota, | ||
132 | rawVideoQuotaUsed: user.videoQuotaUsed, | ||
133 | videoQuotaDaily, | ||
134 | videoQuotaUsedDaily, | ||
135 | rawVideoQuotaDaily: user.videoQuotaDaily, | ||
136 | rawVideoQuotaUsedDaily: user.videoQuotaUsedDaily | ||
137 | }) | ||
138 | } | ||
139 | } | ||
diff --git a/client/src/app/shared/shared-users/user-signup.service.ts b/client/src/app/shared/shared-users/user-signup.service.ts new file mode 100644 index 000000000..46fe34af1 --- /dev/null +++ b/client/src/app/shared/shared-users/user-signup.service.ts | |||
@@ -0,0 +1,56 @@ | |||
1 | import { catchError, tap } from 'rxjs/operators' | ||
2 | import { HttpClient } from '@angular/common/http' | ||
3 | import { Injectable } from '@angular/core' | ||
4 | import { RestExtractor, UserService } from '@app/core' | ||
5 | import { UserRegister } from '@shared/models' | ||
6 | |||
7 | @Injectable() | ||
8 | export class UserSignupService { | ||
9 | constructor ( | ||
10 | private authHttp: HttpClient, | ||
11 | private restExtractor: RestExtractor, | ||
12 | private userService: UserService | ||
13 | ) { } | ||
14 | |||
15 | signup (userCreate: UserRegister) { | ||
16 | return this.authHttp.post(UserService.BASE_USERS_URL + 'register', userCreate) | ||
17 | .pipe( | ||
18 | tap(() => this.userService.setSignupInThisSession(true)), | ||
19 | catchError(err => this.restExtractor.handleError(err)) | ||
20 | ) | ||
21 | } | ||
22 | |||
23 | verifyEmail (userId: number, verificationString: string, isPendingEmail: boolean) { | ||
24 | const url = `${UserService.BASE_USERS_URL}/${userId}/verify-email` | ||
25 | const body = { | ||
26 | verificationString, | ||
27 | isPendingEmail | ||
28 | } | ||
29 | |||
30 | return this.authHttp.post(url, body) | ||
31 | .pipe(catchError(res => this.restExtractor.handleError(res))) | ||
32 | } | ||
33 | |||
34 | askSendVerifyEmail (email: string) { | ||
35 | const url = UserService.BASE_USERS_URL + '/ask-send-verify-email' | ||
36 | |||
37 | return this.authHttp.post(url, { email }) | ||
38 | .pipe(catchError(err => this.restExtractor.handleError(err))) | ||
39 | } | ||
40 | |||
41 | getNewUsername (oldDisplayName: string, newDisplayName: string, currentUsername: string) { | ||
42 | // Don't update display name, the user seems to have changed it | ||
43 | if (this.displayNameToUsername(oldDisplayName) !== currentUsername) return currentUsername | ||
44 | |||
45 | return this.displayNameToUsername(newDisplayName) | ||
46 | } | ||
47 | |||
48 | private displayNameToUsername (displayName: string) { | ||
49 | if (!displayName) return '' | ||
50 | |||
51 | return displayName | ||
52 | .toLowerCase() | ||
53 | .replace(/\s/g, '_') | ||
54 | .replace(/[^a-z0-9_.]/g, '') | ||
55 | } | ||
56 | } | ||