diff options
Diffstat (limited to 'client/src/app')
80 files changed, 763 insertions, 256 deletions
diff --git a/client/src/app/+about/about-instance/contact-admin-modal.component.ts b/client/src/app/+about/about-instance/contact-admin-modal.component.ts index fab9cfc4b..0e2bf51e8 100644 --- a/client/src/app/+about/about-instance/contact-admin-modal.component.ts +++ b/client/src/app/+about/about-instance/contact-admin-modal.component.ts | |||
@@ -7,7 +7,7 @@ import { | |||
7 | FROM_NAME_VALIDATOR, | 7 | FROM_NAME_VALIDATOR, |
8 | SUBJECT_VALIDATOR | 8 | SUBJECT_VALIDATOR |
9 | } from '@app/shared/form-validators/instance-validators' | 9 | } from '@app/shared/form-validators/instance-validators' |
10 | import { FormReactive, FormValidatorService } from '@app/shared/shared-forms' | 10 | import { FormReactive, FormReactiveService } from '@app/shared/shared-forms' |
11 | import { InstanceService } from '@app/shared/shared-instance' | 11 | import { InstanceService } from '@app/shared/shared-instance' |
12 | import { NgbModal } from '@ng-bootstrap/ng-bootstrap' | 12 | import { NgbModal } from '@ng-bootstrap/ng-bootstrap' |
13 | import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap/modal/modal-ref' | 13 | import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap/modal/modal-ref' |
@@ -32,7 +32,7 @@ export class ContactAdminModalComponent extends FormReactive implements OnInit { | |||
32 | private serverConfig: HTMLServerConfig | 32 | private serverConfig: HTMLServerConfig |
33 | 33 | ||
34 | constructor ( | 34 | constructor ( |
35 | protected formValidatorService: FormValidatorService, | 35 | protected formReactiveService: FormReactiveService, |
36 | private router: Router, | 36 | private router: Router, |
37 | private modalService: NgbModal, | 37 | private modalService: NgbModal, |
38 | private instanceService: InstanceService, | 38 | private instanceService: InstanceService, |
diff --git a/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.ts b/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.ts index 545e37857..168f4702c 100644 --- a/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.ts +++ b/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.ts | |||
@@ -18,15 +18,15 @@ import { | |||
18 | MAX_INSTANCE_LIVES_VALIDATOR, | 18 | MAX_INSTANCE_LIVES_VALIDATOR, |
19 | MAX_LIVE_DURATION_VALIDATOR, | 19 | MAX_LIVE_DURATION_VALIDATOR, |
20 | MAX_USER_LIVES_VALIDATOR, | 20 | MAX_USER_LIVES_VALIDATOR, |
21 | MAX_VIDEO_CHANNELS_PER_USER_VALIDATOR, | ||
21 | SEARCH_INDEX_URL_VALIDATOR, | 22 | SEARCH_INDEX_URL_VALIDATOR, |
22 | SERVICES_TWITTER_USERNAME_VALIDATOR, | 23 | SERVICES_TWITTER_USERNAME_VALIDATOR, |
23 | SIGNUP_LIMIT_VALIDATOR, | 24 | SIGNUP_LIMIT_VALIDATOR, |
24 | SIGNUP_MINIMUM_AGE_VALIDATOR, | 25 | SIGNUP_MINIMUM_AGE_VALIDATOR, |
25 | TRANSCODING_THREADS_VALIDATOR, | 26 | TRANSCODING_THREADS_VALIDATOR |
26 | MAX_VIDEO_CHANNELS_PER_USER_VALIDATOR | ||
27 | } from '@app/shared/form-validators/custom-config-validators' | 27 | } from '@app/shared/form-validators/custom-config-validators' |
28 | import { USER_VIDEO_QUOTA_DAILY_VALIDATOR, USER_VIDEO_QUOTA_VALIDATOR } from '@app/shared/form-validators/user-validators' | 28 | import { USER_VIDEO_QUOTA_DAILY_VALIDATOR, USER_VIDEO_QUOTA_VALIDATOR } from '@app/shared/form-validators/user-validators' |
29 | import { FormReactive, FormValidatorService } from '@app/shared/shared-forms' | 29 | import { FormReactive, FormReactiveService } from '@app/shared/shared-forms' |
30 | import { CustomPageService } from '@app/shared/shared-main/custom-page' | 30 | import { CustomPageService } from '@app/shared/shared-main/custom-page' |
31 | import { CustomConfig, CustomPage, HTMLServerConfig } from '@shared/models' | 31 | import { CustomConfig, CustomPage, HTMLServerConfig } from '@shared/models' |
32 | import { EditConfigurationService } from './edit-configuration.service' | 32 | import { EditConfigurationService } from './edit-configuration.service' |
@@ -52,9 +52,9 @@ export class EditCustomConfigComponent extends FormReactive implements OnInit { | |||
52 | categoryItems: SelectOptionsItem[] = [] | 52 | categoryItems: SelectOptionsItem[] = [] |
53 | 53 | ||
54 | constructor ( | 54 | constructor ( |
55 | protected formReactiveService: FormReactiveService, | ||
55 | private router: Router, | 56 | private router: Router, |
56 | private route: ActivatedRoute, | 57 | private route: ActivatedRoute, |
57 | protected formValidatorService: FormValidatorService, | ||
58 | private notifier: Notifier, | 58 | private notifier: Notifier, |
59 | private configService: ConfigService, | 59 | private configService: ConfigService, |
60 | private customPage: CustomPageService, | 60 | private customPage: CustomPageService, |
diff --git a/client/src/app/+admin/follows/following-list/follow-modal.component.ts b/client/src/app/+admin/follows/following-list/follow-modal.component.ts index 07cc75d77..8f74e82a6 100644 --- a/client/src/app/+admin/follows/following-list/follow-modal.component.ts +++ b/client/src/app/+admin/follows/following-list/follow-modal.component.ts | |||
@@ -2,7 +2,7 @@ import { Component, EventEmitter, OnInit, Output, ViewChild } from '@angular/cor | |||
2 | import { Notifier } from '@app/core' | 2 | import { Notifier } from '@app/core' |
3 | import { prepareIcu } from '@app/helpers' | 3 | import { prepareIcu } from '@app/helpers' |
4 | import { splitAndGetNotEmpty, UNIQUE_HOSTS_OR_HANDLE_VALIDATOR } from '@app/shared/form-validators/host-validators' | 4 | import { splitAndGetNotEmpty, UNIQUE_HOSTS_OR_HANDLE_VALIDATOR } from '@app/shared/form-validators/host-validators' |
5 | import { FormReactive, FormValidatorService } from '@app/shared/shared-forms' | 5 | import { FormReactive, FormReactiveService } from '@app/shared/shared-forms' |
6 | import { InstanceFollowService } from '@app/shared/shared-instance' | 6 | import { InstanceFollowService } from '@app/shared/shared-instance' |
7 | import { NgbModal } from '@ng-bootstrap/ng-bootstrap' | 7 | import { NgbModal } from '@ng-bootstrap/ng-bootstrap' |
8 | import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap/modal/modal-ref' | 8 | import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap/modal/modal-ref' |
@@ -22,7 +22,7 @@ export class FollowModalComponent extends FormReactive implements OnInit { | |||
22 | private openedModal: NgbModalRef | 22 | private openedModal: NgbModalRef |
23 | 23 | ||
24 | constructor ( | 24 | constructor ( |
25 | protected formValidatorService: FormValidatorService, | 25 | protected formReactiveService: FormReactiveService, |
26 | private modalService: NgbModal, | 26 | private modalService: NgbModal, |
27 | private followService: InstanceFollowService, | 27 | private followService: InstanceFollowService, |
28 | private notifier: Notifier | 28 | private notifier: Notifier |
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 1713e06ce..0627aa887 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 | |||
@@ -12,7 +12,7 @@ import { | |||
12 | USER_VIDEO_QUOTA_DAILY_VALIDATOR, | 12 | USER_VIDEO_QUOTA_DAILY_VALIDATOR, |
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 { FormReactiveService } from '@app/shared/shared-forms' |
16 | import { UserAdminService } from '@app/shared/shared-users' | 16 | import { UserAdminService } from '@app/shared/shared-users' |
17 | import { UserCreate, UserRole } from '@shared/models' | 17 | import { UserCreate, UserRole } from '@shared/models' |
18 | import { UserEdit } from './user-edit' | 18 | import { UserEdit } from './user-edit' |
@@ -27,7 +27,7 @@ export class UserCreateComponent extends UserEdit implements OnInit { | |||
27 | 27 | ||
28 | constructor ( | 28 | constructor ( |
29 | protected serverService: ServerService, | 29 | protected serverService: ServerService, |
30 | protected formValidatorService: FormValidatorService, | 30 | protected formReactiveService: FormReactiveService, |
31 | protected configService: ConfigService, | 31 | protected configService: ConfigService, |
32 | protected screenService: ScreenService, | 32 | protected screenService: ScreenService, |
33 | protected auth: AuthService, | 33 | protected auth: AuthService, |
diff --git a/client/src/app/+admin/overview/users/user-edit/user-edit.component.html b/client/src/app/+admin/overview/users/user-edit/user-edit.component.html index da5879a36..e51ccf808 100644 --- a/client/src/app/+admin/overview/users/user-edit/user-edit.component.html +++ b/client/src/app/+admin/overview/users/user-edit/user-edit.component.html | |||
@@ -204,7 +204,7 @@ | |||
204 | </div> | 204 | </div> |
205 | 205 | ||
206 | 206 | ||
207 | <div *ngIf="!isCreation() && user && user.pluginAuth === null" class="row mt-4"> <!-- danger zone grid --> | 207 | <div *ngIf="displayDangerZone()" class="row mt-4"> <!-- danger zone grid --> |
208 | <div class="col-12 col-lg-4 col-xl-3"> | 208 | <div class="col-12 col-lg-4 col-xl-3"> |
209 | <div class="anchor" id="danger"></div> <!-- danger zone anchor --> | 209 | <div class="anchor" id="danger"></div> <!-- danger zone anchor --> |
210 | <div i18n class="account-title account-title-danger">DANGER ZONE</div> | 210 | <div i18n class="account-title account-title-danger">DANGER ZONE</div> |
@@ -213,7 +213,7 @@ | |||
213 | <div class="col-12 col-lg-8 col-xl-9"> | 213 | <div class="col-12 col-lg-8 col-xl-9"> |
214 | 214 | ||
215 | <div class="danger-zone"> | 215 | <div class="danger-zone"> |
216 | <div class="form-group reset-password-email"> | 216 | <div class="form-group"> |
217 | <label i18n>Send a link to reset the password by email to the user</label> | 217 | <label i18n>Send a link to reset the password by email to the user</label> |
218 | <button (click)="resetPassword()" i18n>Ask for new password</button> | 218 | <button (click)="resetPassword()" i18n>Ask for new password</button> |
219 | </div> | 219 | </div> |
@@ -222,6 +222,11 @@ | |||
222 | <label i18n>Manually set the user password</label> | 222 | <label i18n>Manually set the user password</label> |
223 | <my-user-password [userId]="user.id"></my-user-password> | 223 | <my-user-password [userId]="user.id"></my-user-password> |
224 | </div> | 224 | </div> |
225 | |||
226 | <div *ngIf="user.twoFactorEnabled" class="form-group"> | ||
227 | <label i18n>This user has two factor authentication enabled</label> | ||
228 | <button (click)="disableTwoFactorAuth()" i18n>Disable two factor authentication</button> | ||
229 | </div> | ||
225 | </div> | 230 | </div> |
226 | 231 | ||
227 | </div> | 232 | </div> |
diff --git a/client/src/app/+admin/overview/users/user-edit/user-edit.component.scss b/client/src/app/+admin/overview/users/user-edit/user-edit.component.scss index 68fa1215f..698628149 100644 --- a/client/src/app/+admin/overview/users/user-edit/user-edit.component.scss +++ b/client/src/app/+admin/overview/users/user-edit/user-edit.component.scss | |||
@@ -48,17 +48,13 @@ my-user-real-quota-info { | |||
48 | } | 48 | } |
49 | 49 | ||
50 | .danger-zone { | 50 | .danger-zone { |
51 | .reset-password-email { | 51 | button { |
52 | margin-bottom: 30px; | 52 | @include peertube-button; |
53 | @include danger-button; | ||
54 | @include disable-outline; | ||
53 | 55 | ||
54 | button { | 56 | display: block; |
55 | @include peertube-button; | 57 | margin-top: 0; |
56 | @include danger-button; | ||
57 | @include disable-outline; | ||
58 | |||
59 | display: block; | ||
60 | margin-top: 0; | ||
61 | } | ||
62 | } | 58 | } |
63 | } | 59 | } |
64 | 60 | ||
diff --git a/client/src/app/+admin/overview/users/user-edit/user-edit.ts b/client/src/app/+admin/overview/users/user-edit/user-edit.ts index 6dae4110d..21e9629ab 100644 --- a/client/src/app/+admin/overview/users/user-edit/user-edit.ts +++ b/client/src/app/+admin/overview/users/user-edit/user-edit.ts | |||
@@ -60,10 +60,22 @@ export abstract class UserEdit extends FormReactive implements OnInit { | |||
60 | ] | 60 | ] |
61 | } | 61 | } |
62 | 62 | ||
63 | displayDangerZone () { | ||
64 | if (this.isCreation()) return false | ||
65 | if (this.user?.pluginAuth) return false | ||
66 | if (this.auth.getUser().id === this.user.id) return false | ||
67 | |||
68 | return true | ||
69 | } | ||
70 | |||
63 | resetPassword () { | 71 | resetPassword () { |
64 | return | 72 | return |
65 | } | 73 | } |
66 | 74 | ||
75 | disableTwoFactorAuth () { | ||
76 | return | ||
77 | } | ||
78 | |||
67 | getUserVideoQuota () { | 79 | getUserVideoQuota () { |
68 | return this.form.value['videoQuota'] | 80 | return this.form.value['videoQuota'] |
69 | } | 81 | } |
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 8999d1f00..d6616e077 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,7 @@ | |||
1 | import { Component, Input, OnInit } from '@angular/core' | 1 | import { Component, Input, OnInit } from '@angular/core' |
2 | import { Notifier } 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, FormReactiveService } from '@app/shared/shared-forms' |
5 | import { UserAdminService } from '@app/shared/shared-users' | 5 | import { UserAdminService } from '@app/shared/shared-users' |
6 | import { UserUpdate } from '@shared/models' | 6 | import { UserUpdate } from '@shared/models' |
7 | 7 | ||
@@ -18,7 +18,7 @@ export class UserPasswordComponent extends FormReactive implements OnInit { | |||
18 | @Input() userId: number | 18 | @Input() userId: number |
19 | 19 | ||
20 | constructor ( | 20 | constructor ( |
21 | protected formValidatorService: FormValidatorService, | 21 | protected formReactiveService: FormReactiveService, |
22 | private notifier: Notifier, | 22 | private notifier: Notifier, |
23 | private userAdminService: UserAdminService | 23 | private userAdminService: UserAdminService |
24 | ) { | 24 | ) { |
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 bab288a67..71212b19c 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 | |||
@@ -9,8 +9,8 @@ import { | |||
9 | USER_VIDEO_QUOTA_DAILY_VALIDATOR, | 9 | USER_VIDEO_QUOTA_DAILY_VALIDATOR, |
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 { FormReactiveService } from '@app/shared/shared-forms' |
13 | import { UserAdminService } from '@app/shared/shared-users' | 13 | import { TwoFactorService, UserAdminService } from '@app/shared/shared-users' |
14 | import { User as UserType, UserAdminFlag, UserRole, UserUpdate } from '@shared/models' | 14 | import { User as UserType, UserAdminFlag, UserRole, UserUpdate } from '@shared/models' |
15 | import { UserEdit } from './user-edit' | 15 | import { UserEdit } from './user-edit' |
16 | 16 | ||
@@ -25,7 +25,7 @@ export class UserUpdateComponent extends UserEdit implements OnInit, OnDestroy { | |||
25 | private paramsSub: Subscription | 25 | private paramsSub: Subscription |
26 | 26 | ||
27 | constructor ( | 27 | constructor ( |
28 | protected formValidatorService: FormValidatorService, | 28 | protected formReactiveService: FormReactiveService, |
29 | protected serverService: ServerService, | 29 | protected serverService: ServerService, |
30 | protected configService: ConfigService, | 30 | protected configService: ConfigService, |
31 | protected screenService: ScreenService, | 31 | protected screenService: ScreenService, |
@@ -34,6 +34,7 @@ export class UserUpdateComponent extends UserEdit implements OnInit, OnDestroy { | |||
34 | private router: Router, | 34 | private router: Router, |
35 | private notifier: Notifier, | 35 | private notifier: Notifier, |
36 | private userService: UserService, | 36 | private userService: UserService, |
37 | private twoFactorService: TwoFactorService, | ||
37 | private userAdminService: UserAdminService | 38 | private userAdminService: UserAdminService |
38 | ) { | 39 | ) { |
39 | super() | 40 | super() |
@@ -120,10 +121,22 @@ export class UserUpdateComponent extends UserEdit implements OnInit, OnDestroy { | |||
120 | this.notifier.success($localize`An email asking for password reset has been sent to ${this.user.username}.`) | 121 | this.notifier.success($localize`An email asking for password reset has been sent to ${this.user.username}.`) |
121 | }, | 122 | }, |
122 | 123 | ||
123 | error: err => { | 124 | error: err => this.notifier.error(err.message) |
124 | this.error = err.message | 125 | }) |
125 | } | 126 | } |
127 | |||
128 | disableTwoFactorAuth () { | ||
129 | this.twoFactorService.disableTwoFactor({ userId: this.user.id }) | ||
130 | .subscribe({ | ||
131 | next: () => { | ||
132 | this.user.twoFactorEnabled = false | ||
133 | |||
134 | this.notifier.success($localize`Two factor authentication of ${this.user.username} disabled.`) | ||
135 | }, | ||
136 | |||
137 | error: err => this.notifier.error(err.message) | ||
126 | }) | 138 | }) |
139 | |||
127 | } | 140 | } |
128 | 141 | ||
129 | private onUserFetched (userJson: UserType) { | 142 | private onUserFetched (userJson: UserType) { |
diff --git a/client/src/app/+admin/overview/users/user-list/user-list.component.scss b/client/src/app/+admin/overview/users/user-list/user-list.component.scss index 3c775cac5..23e0d29ee 100644 --- a/client/src/app/+admin/overview/users/user-list/user-list.component.scss +++ b/client/src/app/+admin/overview/users/user-list/user-list.component.scss | |||
@@ -1,6 +1,6 @@ | |||
1 | @use '_variables' as *; | 1 | @use '_variables' as *; |
2 | @use '_mixins' as *; | 2 | @use '_mixins' as *; |
3 | @use '~bootstrap/scss/functions' as *; | 3 | @use 'bootstrap/scss/functions' as *; |
4 | 4 | ||
5 | .add-button { | 5 | .add-button { |
6 | @include create-button; | 6 | @include create-button; |
diff --git a/client/src/app/+admin/plugins/plugin-show-installed/plugin-show-installed.component.ts b/client/src/app/+admin/plugins/plugin-show-installed/plugin-show-installed.component.ts index ec02cfcd9..b1a41567e 100644 --- a/client/src/app/+admin/plugins/plugin-show-installed/plugin-show-installed.component.ts +++ b/client/src/app/+admin/plugins/plugin-show-installed/plugin-show-installed.component.ts | |||
@@ -4,7 +4,7 @@ import { Component, OnDestroy, OnInit } from '@angular/core' | |||
4 | import { ActivatedRoute } from '@angular/router' | 4 | import { ActivatedRoute } from '@angular/router' |
5 | import { HooksService, Notifier, PluginService } from '@app/core' | 5 | import { HooksService, Notifier, PluginService } from '@app/core' |
6 | import { BuildFormArgument } from '@app/shared/form-validators' | 6 | import { BuildFormArgument } from '@app/shared/form-validators' |
7 | import { FormReactive, FormValidatorService } from '@app/shared/shared-forms' | 7 | import { FormReactive, FormReactiveService } from '@app/shared/shared-forms' |
8 | import { PeerTubePlugin, RegisterServerSettingOptions } from '@shared/models' | 8 | import { PeerTubePlugin, RegisterServerSettingOptions } from '@shared/models' |
9 | import { PluginApiService } from '../shared/plugin-api.service' | 9 | import { PluginApiService } from '../shared/plugin-api.service' |
10 | 10 | ||
@@ -22,7 +22,7 @@ export class PluginShowInstalledComponent extends FormReactive implements OnInit | |||
22 | private npmName: string | 22 | private npmName: string |
23 | 23 | ||
24 | constructor ( | 24 | constructor ( |
25 | protected formValidatorService: FormValidatorService, | 25 | protected formReactiveService: FormReactiveService, |
26 | private pluginService: PluginService, | 26 | private pluginService: PluginService, |
27 | private pluginAPIService: PluginApiService, | 27 | private pluginAPIService: PluginApiService, |
28 | private notifier: Notifier, | 28 | private notifier: Notifier, |
diff --git a/client/src/app/+login/login.component.html b/client/src/app/+login/login.component.html index f3a2476f9..49b443a20 100644 --- a/client/src/app/+login/login.component.html +++ b/client/src/app/+login/login.component.html | |||
@@ -39,34 +39,48 @@ | |||
39 | <div class="login-form-and-externals"> | 39 | <div class="login-form-and-externals"> |
40 | 40 | ||
41 | <form myPluginSelector pluginSelectorId="login-form" role="form" (ngSubmit)="login()" [formGroup]="form"> | 41 | <form myPluginSelector pluginSelectorId="login-form" role="form" (ngSubmit)="login()" [formGroup]="form"> |
42 | <div class="form-group"> | 42 | <ng-container *ngIf="!otpStep"> |
43 | <div> | 43 | <div class="form-group"> |
44 | <label i18n for="username">Username or email address</label> | 44 | <div> |
45 | <input | 45 | <label i18n for="username">Username or email address</label> |
46 | type="text" id="username" i18n-placeholder placeholder="Example: john@example.com" required tabindex="1" | 46 | <input |
47 | formControlName="username" class="form-control" [ngClass]="{ 'input-error': formErrors['username'] }" myAutofocus | 47 | type="text" id="username" i18n-placeholder placeholder="Example: john@example.com" required tabindex="1" |
48 | > | 48 | formControlName="username" class="form-control" [ngClass]="{ 'input-error': formErrors['username'] }" myAutofocus |
49 | > | ||
50 | </div> | ||
51 | |||
52 | <div *ngIf="formErrors.username" class="form-error">{{ formErrors.username }}</div> | ||
53 | |||
54 | <div *ngIf="hasUsernameUppercase()" i18n class="form-warning"> | ||
55 | ⚠️ Most email addresses do not include capital letters. | ||
56 | </div> | ||
49 | </div> | 57 | </div> |
50 | 58 | ||
51 | <div *ngIf="formErrors.username" class="form-error">{{ formErrors.username }}</div> | 59 | <div class="form-group"> |
60 | <label i18n for="password">Password</label> | ||
52 | 61 | ||
53 | <div *ngIf="hasUsernameUppercase()" i18n class="form-warning"> | 62 | <my-input-text |
54 | ⚠️ Most email addresses do not include capital letters. | 63 | formControlName="password" inputId="password" i18n-placeholder placeholder="Password" |
64 | [formError]="formErrors['password']" autocomplete="current-password" [tabindex]="2" | ||
65 | ></my-input-text> | ||
55 | </div> | 66 | </div> |
56 | </div> | 67 | </ng-container> |
68 | |||
69 | <div *ngIf="otpStep" class="form-group"> | ||
70 | <p i18n>Enter the two-factor code generated by your phone app:</p> | ||
57 | 71 | ||
58 | <div class="form-group"> | 72 | <label i18n for="otp-token">Two factor authentication token</label> |
59 | <label i18n for="password">Password</label> | ||
60 | 73 | ||
61 | <my-input-text | 74 | <my-input-text |
62 | formControlName="password" inputId="password" i18n-placeholder placeholder="Password" | 75 | #otpTokenInput |
63 | [formError]="formErrors['password']" autocomplete="current-password" [tabindex]="2" | 76 | [show]="true" formControlName="otp-token" inputId="otp-token" |
77 | [formError]="formErrors['otp-token']" autocomplete="otp-token" | ||
64 | ></my-input-text> | 78 | ></my-input-text> |
65 | </div> | 79 | </div> |
66 | 80 | ||
67 | <input type="submit" class="peertube-button orange-button" i18n-value value="Login" [disabled]="!form.valid"> | 81 | <input type="submit" class="peertube-button orange-button" i18n-value value="Login" [disabled]="!form.valid"> |
68 | 82 | ||
69 | <div class="additional-links"> | 83 | <div *ngIf="!otpStep" class="additional-links"> |
70 | <a i18n role="button" class="link-orange" (click)="openForgotPasswordModal()" i18n-title title="Click here to reset your password">I forgot my password</a> | 84 | <a i18n role="button" class="link-orange" (click)="openForgotPasswordModal()" i18n-title title="Click here to reset your password">I forgot my password</a> |
71 | 85 | ||
72 | <ng-container *ngIf="signupAllowed"> | 86 | <ng-container *ngIf="signupAllowed"> |
diff --git a/client/src/app/+login/login.component.scss b/client/src/app/+login/login.component.scss index d31d428f7..17e151fd8 100644 --- a/client/src/app/+login/login.component.scss +++ b/client/src/app/+login/login.component.scss | |||
@@ -1,8 +1,8 @@ | |||
1 | @use '_variables' as *; | 1 | @use '_variables' as *; |
2 | @use '_mixins' as *; | 2 | @use '_mixins' as *; |
3 | 3 | ||
4 | @import '~bootstrap/scss/functions'; | 4 | @import 'bootstrap/scss/functions'; |
5 | @import '~bootstrap/scss/variables'; | 5 | @import 'bootstrap/scss/variables'; |
6 | 6 | ||
7 | label { | 7 | label { |
8 | display: block; | 8 | display: block; |
diff --git a/client/src/app/+login/login.component.ts b/client/src/app/+login/login.component.ts index 2ed9be16c..c1705807f 100644 --- a/client/src/app/+login/login.component.ts +++ b/client/src/app/+login/login.component.ts | |||
@@ -1,10 +1,10 @@ | |||
1 | |||
2 | import { AfterViewInit, Component, ElementRef, OnInit, ViewChild } from '@angular/core' | 1 | import { AfterViewInit, Component, ElementRef, OnInit, ViewChild } from '@angular/core' |
3 | import { ActivatedRoute, Router } from '@angular/router' | 2 | import { ActivatedRoute, Router } from '@angular/router' |
4 | import { AuthService, Notifier, RedirectService, SessionStorageService, UserService } from '@app/core' | 3 | import { AuthService, Notifier, RedirectService, SessionStorageService, UserService } from '@app/core' |
5 | import { HooksService } from '@app/core/plugins/hooks.service' | 4 | import { HooksService } from '@app/core/plugins/hooks.service' |
6 | import { LOGIN_PASSWORD_VALIDATOR, LOGIN_USERNAME_VALIDATOR } from '@app/shared/form-validators/login-validators' | 5 | import { LOGIN_PASSWORD_VALIDATOR, LOGIN_USERNAME_VALIDATOR } from '@app/shared/form-validators/login-validators' |
7 | import { FormReactive, FormValidatorService } from '@app/shared/shared-forms' | 6 | import { USER_OTP_TOKEN_VALIDATOR } from '@app/shared/form-validators/user-validators' |
7 | import { FormReactive, FormReactiveService, InputTextComponent } from '@app/shared/shared-forms' | ||
8 | import { InstanceAboutAccordionComponent } from '@app/shared/shared-instance' | 8 | import { InstanceAboutAccordionComponent } from '@app/shared/shared-instance' |
9 | import { NgbAccordion, NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap' | 9 | import { NgbAccordion, NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap' |
10 | import { PluginsManager } from '@root-helpers/plugins-manager' | 10 | import { PluginsManager } from '@root-helpers/plugins-manager' |
@@ -20,6 +20,7 @@ export class LoginComponent extends FormReactive implements OnInit, AfterViewIni | |||
20 | private static SESSION_STORAGE_REDIRECT_URL_KEY = 'login-previous-url' | 20 | private static SESSION_STORAGE_REDIRECT_URL_KEY = 'login-previous-url' |
21 | 21 | ||
22 | @ViewChild('forgotPasswordModal', { static: true }) forgotPasswordModal: ElementRef | 22 | @ViewChild('forgotPasswordModal', { static: true }) forgotPasswordModal: ElementRef |
23 | @ViewChild('otpTokenInput') otpTokenInput: InputTextComponent | ||
23 | 24 | ||
24 | accordion: NgbAccordion | 25 | accordion: NgbAccordion |
25 | error: string = null | 26 | error: string = null |
@@ -37,11 +38,13 @@ export class LoginComponent extends FormReactive implements OnInit, AfterViewIni | |||
37 | codeOfConduct: false | 38 | codeOfConduct: false |
38 | } | 39 | } |
39 | 40 | ||
41 | otpStep = false | ||
42 | |||
40 | private openedForgotPasswordModal: NgbModalRef | 43 | private openedForgotPasswordModal: NgbModalRef |
41 | private serverConfig: ServerConfig | 44 | private serverConfig: ServerConfig |
42 | 45 | ||
43 | constructor ( | 46 | constructor ( |
44 | protected formValidatorService: FormValidatorService, | 47 | protected formReactiveService: FormReactiveService, |
45 | private route: ActivatedRoute, | 48 | private route: ActivatedRoute, |
46 | private modalService: NgbModal, | 49 | private modalService: NgbModal, |
47 | private authService: AuthService, | 50 | private authService: AuthService, |
@@ -82,7 +85,11 @@ export class LoginComponent extends FormReactive implements OnInit, AfterViewIni | |||
82 | // Avoid undefined errors when accessing form error properties | 85 | // Avoid undefined errors when accessing form error properties |
83 | this.buildForm({ | 86 | this.buildForm({ |
84 | username: LOGIN_USERNAME_VALIDATOR, | 87 | username: LOGIN_USERNAME_VALIDATOR, |
85 | password: LOGIN_PASSWORD_VALIDATOR | 88 | password: LOGIN_PASSWORD_VALIDATOR, |
89 | 'otp-token': { | ||
90 | VALIDATORS: [], // Will be set dynamically | ||
91 | MESSAGES: USER_OTP_TOKEN_VALIDATOR.MESSAGES | ||
92 | } | ||
86 | }) | 93 | }) |
87 | 94 | ||
88 | this.serverConfig = snapshot.data.serverConfig | 95 | this.serverConfig = snapshot.data.serverConfig |
@@ -118,13 +125,20 @@ export class LoginComponent extends FormReactive implements OnInit, AfterViewIni | |||
118 | login () { | 125 | login () { |
119 | this.error = null | 126 | this.error = null |
120 | 127 | ||
121 | const { username, password } = this.form.value | 128 | const options = { |
129 | username: this.form.value['username'], | ||
130 | password: this.form.value['password'], | ||
131 | otpToken: this.form.value['otp-token'] | ||
132 | } | ||
122 | 133 | ||
123 | this.authService.login(username, password) | 134 | this.authService.login(options) |
135 | .pipe() | ||
124 | .subscribe({ | 136 | .subscribe({ |
125 | next: () => this.redirectService.redirectToPreviousRoute(), | 137 | next: () => this.redirectService.redirectToPreviousRoute(), |
126 | 138 | ||
127 | error: err => this.handleError(err) | 139 | error: err => { |
140 | this.handleError(err) | ||
141 | } | ||
128 | }) | 142 | }) |
129 | } | 143 | } |
130 | 144 | ||
@@ -162,7 +176,7 @@ The link will expire within 1 hour.` | |||
162 | private loadExternalAuthToken (username: string, token: string) { | 176 | private loadExternalAuthToken (username: string, token: string) { |
163 | this.isAuthenticatedWithExternalAuth = true | 177 | this.isAuthenticatedWithExternalAuth = true |
164 | 178 | ||
165 | this.authService.login(username, null, token) | 179 | this.authService.login({ username, password: null, token }) |
166 | .subscribe({ | 180 | .subscribe({ |
167 | next: () => { | 181 | next: () => { |
168 | const redirectUrl = this.storage.getItem(LoginComponent.SESSION_STORAGE_REDIRECT_URL_KEY) | 182 | const redirectUrl = this.storage.getItem(LoginComponent.SESSION_STORAGE_REDIRECT_URL_KEY) |
@@ -182,6 +196,17 @@ The link will expire within 1 hour.` | |||
182 | } | 196 | } |
183 | 197 | ||
184 | private handleError (err: any) { | 198 | private handleError (err: any) { |
199 | if (this.authService.isOTPMissingError(err)) { | ||
200 | this.otpStep = true | ||
201 | |||
202 | setTimeout(() => { | ||
203 | this.form.get('otp-token').setValidators(USER_OTP_TOKEN_VALIDATOR.VALIDATORS) | ||
204 | this.otpTokenInput.focus() | ||
205 | }) | ||
206 | |||
207 | return | ||
208 | } | ||
209 | |||
185 | if (err.message.indexOf('credentials are invalid') !== -1) this.error = $localize`Incorrect username or password.` | 210 | if (err.message.indexOf('credentials are invalid') !== -1) this.error = $localize`Incorrect username or password.` |
186 | else if (err.message.indexOf('blocked') !== -1) this.error = $localize`Your account is blocked.` | 211 | else if (err.message.indexOf('blocked') !== -1) this.error = $localize`Your account is blocked.` |
187 | else this.error = err.message | 212 | else this.error = err.message |
diff --git a/client/src/app/+manage/video-channel-edit/video-channel-create.component.ts b/client/src/app/+manage/video-channel-edit/video-channel-create.component.ts index 8211451a4..372066890 100644 --- a/client/src/app/+manage/video-channel-edit/video-channel-create.component.ts +++ b/client/src/app/+manage/video-channel-edit/video-channel-create.component.ts | |||
@@ -9,7 +9,7 @@ import { | |||
9 | VIDEO_CHANNEL_NAME_VALIDATOR, | 9 | VIDEO_CHANNEL_NAME_VALIDATOR, |
10 | VIDEO_CHANNEL_SUPPORT_VALIDATOR | 10 | VIDEO_CHANNEL_SUPPORT_VALIDATOR |
11 | } from '@app/shared/form-validators/video-channel-validators' | 11 | } from '@app/shared/form-validators/video-channel-validators' |
12 | import { FormValidatorService } from '@app/shared/shared-forms' | 12 | import { FormReactiveService } from '@app/shared/shared-forms' |
13 | import { VideoChannel, VideoChannelService } from '@app/shared/shared-main' | 13 | import { VideoChannel, VideoChannelService } from '@app/shared/shared-main' |
14 | import { HttpStatusCode, VideoChannelCreate } from '@shared/models' | 14 | import { HttpStatusCode, VideoChannelCreate } from '@shared/models' |
15 | import { VideoChannelEdit } from './video-channel-edit' | 15 | import { VideoChannelEdit } from './video-channel-edit' |
@@ -26,7 +26,7 @@ export class VideoChannelCreateComponent extends VideoChannelEdit implements OnI | |||
26 | private banner: FormData | 26 | private banner: FormData |
27 | 27 | ||
28 | constructor ( | 28 | constructor ( |
29 | protected formValidatorService: FormValidatorService, | 29 | protected formReactiveService: FormReactiveService, |
30 | private authService: AuthService, | 30 | private authService: AuthService, |
31 | private notifier: Notifier, | 31 | private notifier: Notifier, |
32 | private router: Router, | 32 | private router: Router, |
diff --git a/client/src/app/+manage/video-channel-edit/video-channel-update.component.ts b/client/src/app/+manage/video-channel-edit/video-channel-update.component.ts index 7e8d6ffe6..32f6d650d 100644 --- a/client/src/app/+manage/video-channel-edit/video-channel-update.component.ts +++ b/client/src/app/+manage/video-channel-edit/video-channel-update.component.ts | |||
@@ -9,7 +9,7 @@ import { | |||
9 | VIDEO_CHANNEL_DISPLAY_NAME_VALIDATOR, | 9 | VIDEO_CHANNEL_DISPLAY_NAME_VALIDATOR, |
10 | VIDEO_CHANNEL_SUPPORT_VALIDATOR | 10 | VIDEO_CHANNEL_SUPPORT_VALIDATOR |
11 | } from '@app/shared/form-validators/video-channel-validators' | 11 | } from '@app/shared/form-validators/video-channel-validators' |
12 | import { FormValidatorService } from '@app/shared/shared-forms' | 12 | import { FormReactiveService } from '@app/shared/shared-forms' |
13 | import { VideoChannel, VideoChannelService } from '@app/shared/shared-main' | 13 | import { VideoChannel, VideoChannelService } from '@app/shared/shared-main' |
14 | import { HTMLServerConfig, VideoChannelUpdate } from '@shared/models' | 14 | import { HTMLServerConfig, VideoChannelUpdate } from '@shared/models' |
15 | import { VideoChannelEdit } from './video-channel-edit' | 15 | import { VideoChannelEdit } from './video-channel-edit' |
@@ -28,7 +28,7 @@ export class VideoChannelUpdateComponent extends VideoChannelEdit implements OnI | |||
28 | private serverConfig: HTMLServerConfig | 28 | private serverConfig: HTMLServerConfig |
29 | 29 | ||
30 | constructor ( | 30 | constructor ( |
31 | protected formValidatorService: FormValidatorService, | 31 | protected formReactiveService: FormReactiveService, |
32 | private authService: AuthService, | 32 | private authService: AuthService, |
33 | private notifier: Notifier, | 33 | private notifier: Notifier, |
34 | private route: ActivatedRoute, | 34 | private route: ActivatedRoute, |
diff --git a/client/src/app/+my-account/my-account-routing.module.ts b/client/src/app/+my-account/my-account-routing.module.ts index ef39c1a36..b39b1f6b4 100644 --- a/client/src/app/+my-account/my-account-routing.module.ts +++ b/client/src/app/+my-account/my-account-routing.module.ts | |||
@@ -7,6 +7,7 @@ import { MyAccountBlocklistComponent } from './my-account-blocklist/my-account-b | |||
7 | import { MyAccountServerBlocklistComponent } from './my-account-blocklist/my-account-server-blocklist.component' | 7 | import { MyAccountServerBlocklistComponent } from './my-account-blocklist/my-account-server-blocklist.component' |
8 | import { MyAccountNotificationsComponent } from './my-account-notifications/my-account-notifications.component' | 8 | import { MyAccountNotificationsComponent } from './my-account-notifications/my-account-notifications.component' |
9 | import { MyAccountSettingsComponent } from './my-account-settings/my-account-settings.component' | 9 | import { MyAccountSettingsComponent } from './my-account-settings/my-account-settings.component' |
10 | import { MyAccountTwoFactorComponent } from './my-account-settings/my-account-two-factor' | ||
10 | import { MyAccountComponent } from './my-account.component' | 11 | import { MyAccountComponent } from './my-account.component' |
11 | 12 | ||
12 | const myAccountRoutes: Routes = [ | 13 | const myAccountRoutes: Routes = [ |
@@ -31,6 +32,16 @@ const myAccountRoutes: Routes = [ | |||
31 | }, | 32 | }, |
32 | 33 | ||
33 | { | 34 | { |
35 | path: 'two-factor-auth', | ||
36 | component: MyAccountTwoFactorComponent, | ||
37 | data: { | ||
38 | meta: { | ||
39 | title: $localize`Two factor authentication` | ||
40 | } | ||
41 | } | ||
42 | }, | ||
43 | |||
44 | { | ||
34 | path: 'video-channels', | 45 | path: 'video-channels', |
35 | redirectTo: '/my-library/video-channels', | 46 | redirectTo: '/my-library/video-channels', |
36 | pathMatch: 'full' | 47 | pathMatch: 'full' |
diff --git a/client/src/app/+my-account/my-account-settings/my-account-change-email/my-account-change-email.component.ts b/client/src/app/+my-account/my-account-settings/my-account-change-email/my-account-change-email.component.ts index 9b87daa40..235fbec4a 100644 --- a/client/src/app/+my-account/my-account-settings/my-account-change-email/my-account-change-email.component.ts +++ b/client/src/app/+my-account/my-account-settings/my-account-change-email/my-account-change-email.component.ts | |||
@@ -3,8 +3,8 @@ import { tap } from 'rxjs/operators' | |||
3 | import { Component, OnInit } from '@angular/core' | 3 | import { Component, OnInit } from '@angular/core' |
4 | import { AuthService, ServerService, UserService } from '@app/core' | 4 | import { AuthService, ServerService, UserService } from '@app/core' |
5 | import { USER_EMAIL_VALIDATOR, USER_PASSWORD_VALIDATOR } from '@app/shared/form-validators/user-validators' | 5 | import { USER_EMAIL_VALIDATOR, USER_PASSWORD_VALIDATOR } from '@app/shared/form-validators/user-validators' |
6 | import { FormReactive, FormValidatorService } from '@app/shared/shared-forms' | 6 | import { FormReactive, FormReactiveService } from '@app/shared/shared-forms' |
7 | import { User } from '@shared/models' | 7 | import { HttpStatusCode, User } from '@shared/models' |
8 | 8 | ||
9 | @Component({ | 9 | @Component({ |
10 | selector: 'my-account-change-email', | 10 | selector: 'my-account-change-email', |
@@ -17,7 +17,7 @@ export class MyAccountChangeEmailComponent extends FormReactive implements OnIni | |||
17 | user: User = null | 17 | user: User = null |
18 | 18 | ||
19 | constructor ( | 19 | constructor ( |
20 | protected formValidatorService: FormValidatorService, | 20 | protected formReactiveService: FormReactiveService, |
21 | private authService: AuthService, | 21 | private authService: AuthService, |
22 | private userService: UserService, | 22 | private userService: UserService, |
23 | private serverService: ServerService | 23 | private serverService: ServerService |
@@ -57,7 +57,7 @@ export class MyAccountChangeEmailComponent extends FormReactive implements OnIni | |||
57 | }, | 57 | }, |
58 | 58 | ||
59 | error: err => { | 59 | error: err => { |
60 | if (err.status === 401) { | 60 | if (err.status === HttpStatusCode.UNAUTHORIZED_401) { |
61 | this.error = $localize`You current password is invalid.` | 61 | this.error = $localize`You current password is invalid.` |
62 | return | 62 | return |
63 | } | 63 | } |
diff --git a/client/src/app/+my-account/my-account-settings/my-account-change-password/my-account-change-password.component.ts b/client/src/app/+my-account/my-account-settings/my-account-change-password/my-account-change-password.component.ts index 47e54dc23..805d50070 100644 --- a/client/src/app/+my-account/my-account-settings/my-account-change-password/my-account-change-password.component.ts +++ b/client/src/app/+my-account/my-account-settings/my-account-change-password/my-account-change-password.component.ts | |||
@@ -6,8 +6,8 @@ import { | |||
6 | USER_EXISTING_PASSWORD_VALIDATOR, | 6 | USER_EXISTING_PASSWORD_VALIDATOR, |
7 | USER_PASSWORD_VALIDATOR | 7 | USER_PASSWORD_VALIDATOR |
8 | } from '@app/shared/form-validators/user-validators' | 8 | } from '@app/shared/form-validators/user-validators' |
9 | import { FormReactive, FormValidatorService } from '@app/shared/shared-forms' | 9 | import { FormReactive, FormReactiveService } from '@app/shared/shared-forms' |
10 | import { User } from '@shared/models' | 10 | import { HttpStatusCode, User } from '@shared/models' |
11 | 11 | ||
12 | @Component({ | 12 | @Component({ |
13 | selector: 'my-account-change-password', | 13 | selector: 'my-account-change-password', |
@@ -19,7 +19,7 @@ export class MyAccountChangePasswordComponent extends FormReactive implements On | |||
19 | user: User = null | 19 | user: User = null |
20 | 20 | ||
21 | constructor ( | 21 | constructor ( |
22 | protected formValidatorService: FormValidatorService, | 22 | protected formReactiveService: FormReactiveService, |
23 | private notifier: Notifier, | 23 | private notifier: Notifier, |
24 | private authService: AuthService, | 24 | private authService: AuthService, |
25 | private userService: UserService | 25 | private userService: UserService |
@@ -57,7 +57,7 @@ export class MyAccountChangePasswordComponent extends FormReactive implements On | |||
57 | }, | 57 | }, |
58 | 58 | ||
59 | error: err => { | 59 | error: err => { |
60 | if (err.status === 401) { | 60 | if (err.status === HttpStatusCode.UNAUTHORIZED_401) { |
61 | this.error = $localize`You current password is invalid.` | 61 | this.error = $localize`You current password is invalid.` |
62 | return | 62 | return |
63 | } | 63 | } |
diff --git a/client/src/app/+my-account/my-account-settings/my-account-danger-zone/my-account-danger-zone.component.ts b/client/src/app/+my-account/my-account-settings/my-account-danger-zone/my-account-danger-zone.component.ts index 2bae3499e..9619623ee 100644 --- a/client/src/app/+my-account/my-account-settings/my-account-danger-zone/my-account-danger-zone.component.ts +++ b/client/src/app/+my-account/my-account-settings/my-account-danger-zone/my-account-danger-zone.component.ts | |||
@@ -18,7 +18,7 @@ export class MyAccountDangerZoneComponent { | |||
18 | ) { } | 18 | ) { } |
19 | 19 | ||
20 | async deleteMe () { | 20 | async deleteMe () { |
21 | const res = await this.confirmService.confirmWithInput( | 21 | const res = await this.confirmService.confirmWithExpectedInput( |
22 | $localize`Are you sure you want to delete your account?` + | 22 | $localize`Are you sure you want to delete your account?` + |
23 | '<br /><br />' + | 23 | '<br /><br />' + |
24 | // eslint-disable-next-line max-len | 24 | // eslint-disable-next-line max-len |
diff --git a/client/src/app/+my-account/my-account-settings/my-account-profile/my-account-profile.component.ts b/client/src/app/+my-account/my-account-settings/my-account-profile/my-account-profile.component.ts index f395ad73f..8621eb7aa 100644 --- a/client/src/app/+my-account/my-account-settings/my-account-profile/my-account-profile.component.ts +++ b/client/src/app/+my-account/my-account-settings/my-account-profile/my-account-profile.component.ts | |||
@@ -2,7 +2,7 @@ import { Subject } from 'rxjs' | |||
2 | import { Component, Input, OnInit } from '@angular/core' | 2 | import { Component, Input, OnInit } from '@angular/core' |
3 | import { Notifier, User, UserService } from '@app/core' | 3 | import { Notifier, User, UserService } from '@app/core' |
4 | import { USER_DESCRIPTION_VALIDATOR, USER_DISPLAY_NAME_REQUIRED_VALIDATOR } from '@app/shared/form-validators/user-validators' | 4 | import { USER_DESCRIPTION_VALIDATOR, USER_DISPLAY_NAME_REQUIRED_VALIDATOR } from '@app/shared/form-validators/user-validators' |
5 | import { FormReactive, FormValidatorService } from '@app/shared/shared-forms' | 5 | import { FormReactive, FormReactiveService } from '@app/shared/shared-forms' |
6 | 6 | ||
7 | @Component({ | 7 | @Component({ |
8 | selector: 'my-account-profile', | 8 | selector: 'my-account-profile', |
@@ -16,7 +16,7 @@ export class MyAccountProfileComponent extends FormReactive implements OnInit { | |||
16 | error: string = null | 16 | error: string = null |
17 | 17 | ||
18 | constructor ( | 18 | constructor ( |
19 | protected formValidatorService: FormValidatorService, | 19 | protected formReactiveService: FormReactiveService, |
20 | private notifier: Notifier, | 20 | private notifier: Notifier, |
21 | private userService: UserService | 21 | private userService: UserService |
22 | ) { | 22 | ) { |
diff --git a/client/src/app/+my-account/my-account-settings/my-account-settings.component.html b/client/src/app/+my-account/my-account-settings/my-account-settings.component.html index 42a8d0856..666205de6 100644 --- a/client/src/app/+my-account/my-account-settings/my-account-settings.component.html +++ b/client/src/app/+my-account/my-account-settings/my-account-settings.component.html | |||
@@ -62,6 +62,16 @@ | |||
62 | </div> | 62 | </div> |
63 | </div> | 63 | </div> |
64 | 64 | ||
65 | <div class="row mt-5" *ngIf="user.pluginAuth === null"> <!-- two factor auth grid --> | ||
66 | <div class="col-12 col-lg-4 col-xl-3"> | ||
67 | <h2 i18n class="account-title">Two-factor authentication</h2> | ||
68 | </div> | ||
69 | |||
70 | <div class="col-12 col-lg-8 col-xl-9"> | ||
71 | <my-account-two-factor-button [user]="user" [userInformationLoaded]="userInformationLoaded"></my-account-two-factor-button> | ||
72 | </div> | ||
73 | </div> | ||
74 | |||
65 | <div class="row mt-5" *ngIf="user.pluginAuth === null"> <!-- email grid --> | 75 | <div class="row mt-5" *ngIf="user.pluginAuth === null"> <!-- email grid --> |
66 | <div class="col-12 col-lg-4 col-xl-3"> | 76 | <div class="col-12 col-lg-4 col-xl-3"> |
67 | <h2 i18n class="account-title">EMAIL</h2> | 77 | <h2 i18n class="account-title">EMAIL</h2> |
diff --git a/client/src/app/+my-account/my-account-settings/my-account-settings.component.scss b/client/src/app/+my-account/my-account-settings/my-account-settings.component.scss index 8206f4dd8..3d686a146 100644 --- a/client/src/app/+my-account/my-account-settings/my-account-settings.component.scss +++ b/client/src/app/+my-account/my-account-settings/my-account-settings.component.scss | |||
@@ -1,6 +1,6 @@ | |||
1 | @use '_variables' as *; | 1 | @use '_variables' as *; |
2 | @use '_mixins' as *; | 2 | @use '_mixins' as *; |
3 | @use '~bootstrap/scss/functions' as *; | 3 | @use 'bootstrap/scss/functions' as *; |
4 | 4 | ||
5 | .account-title { | 5 | .account-title { |
6 | @include settings-big-title; | 6 | @include settings-big-title; |
diff --git a/client/src/app/+my-account/my-account-settings/my-account-two-factor/index.ts b/client/src/app/+my-account/my-account-settings/my-account-two-factor/index.ts new file mode 100644 index 000000000..cc774bde3 --- /dev/null +++ b/client/src/app/+my-account/my-account-settings/my-account-two-factor/index.ts | |||
@@ -0,0 +1,2 @@ | |||
1 | export * from './my-account-two-factor-button.component' | ||
2 | export * from './my-account-two-factor.component' | ||
diff --git a/client/src/app/+my-account/my-account-settings/my-account-two-factor/my-account-two-factor-button.component.html b/client/src/app/+my-account/my-account-settings/my-account-two-factor/my-account-two-factor-button.component.html new file mode 100644 index 000000000..2fcfffbf3 --- /dev/null +++ b/client/src/app/+my-account/my-account-settings/my-account-two-factor/my-account-two-factor-button.component.html | |||
@@ -0,0 +1,12 @@ | |||
1 | <div class="two-factor"> | ||
2 | <ng-container *ngIf="!twoFactorEnabled"> | ||
3 | <p i18n>Two factor authentication adds an additional layer of security to your account by requiring a numeric code from another device (most commonly mobile phones) when you log in.</p> | ||
4 | |||
5 | <my-button [routerLink]="[ '/my-account/two-factor-auth' ]" className="orange-button-link" i18n>Enable two-factor authentication</my-button> | ||
6 | </ng-container> | ||
7 | |||
8 | <ng-container *ngIf="twoFactorEnabled"> | ||
9 | <my-button className="orange-button" (click)="disableTwoFactor()" i18n>Disable two-factor authentication</my-button> | ||
10 | </ng-container> | ||
11 | |||
12 | </div> | ||
diff --git a/client/src/app/+my-account/my-account-settings/my-account-two-factor/my-account-two-factor-button.component.ts b/client/src/app/+my-account/my-account-settings/my-account-two-factor/my-account-two-factor-button.component.ts new file mode 100644 index 000000000..97ffb6013 --- /dev/null +++ b/client/src/app/+my-account/my-account-settings/my-account-two-factor/my-account-two-factor-button.component.ts | |||
@@ -0,0 +1,49 @@ | |||
1 | import { Subject } from 'rxjs' | ||
2 | import { Component, Input, OnInit } from '@angular/core' | ||
3 | import { AuthService, ConfirmService, Notifier, User } from '@app/core' | ||
4 | import { TwoFactorService } from '@app/shared/shared-users' | ||
5 | |||
6 | @Component({ | ||
7 | selector: 'my-account-two-factor-button', | ||
8 | templateUrl: './my-account-two-factor-button.component.html' | ||
9 | }) | ||
10 | export class MyAccountTwoFactorButtonComponent implements OnInit { | ||
11 | @Input() user: User = null | ||
12 | @Input() userInformationLoaded: Subject<any> | ||
13 | |||
14 | twoFactorEnabled = false | ||
15 | |||
16 | constructor ( | ||
17 | private notifier: Notifier, | ||
18 | private twoFactorService: TwoFactorService, | ||
19 | private confirmService: ConfirmService, | ||
20 | private auth: AuthService | ||
21 | ) { | ||
22 | } | ||
23 | |||
24 | ngOnInit () { | ||
25 | this.userInformationLoaded.subscribe(() => { | ||
26 | this.twoFactorEnabled = this.user.twoFactorEnabled | ||
27 | }) | ||
28 | } | ||
29 | |||
30 | async disableTwoFactor () { | ||
31 | const message = $localize`Are you sure you want to disable two factor authentication of your account?` | ||
32 | |||
33 | const { confirmed, password } = await this.confirmService.confirmWithPassword(message, $localize`Disable two factor`) | ||
34 | if (confirmed === false) return | ||
35 | |||
36 | this.twoFactorService.disableTwoFactor({ userId: this.user.id, currentPassword: password }) | ||
37 | .subscribe({ | ||
38 | next: () => { | ||
39 | this.twoFactorEnabled = false | ||
40 | |||
41 | this.auth.refreshUserInformation() | ||
42 | |||
43 | this.notifier.success($localize`Two factor authentication disabled`) | ||
44 | }, | ||
45 | |||
46 | error: err => this.notifier.error(err.message) | ||
47 | }) | ||
48 | } | ||
49 | } | ||
diff --git a/client/src/app/+my-account/my-account-settings/my-account-two-factor/my-account-two-factor.component.html b/client/src/app/+my-account/my-account-settings/my-account-two-factor/my-account-two-factor.component.html new file mode 100644 index 000000000..16c344e3b --- /dev/null +++ b/client/src/app/+my-account/my-account-settings/my-account-two-factor/my-account-two-factor.component.html | |||
@@ -0,0 +1,54 @@ | |||
1 | <h1> | ||
2 | <my-global-icon iconName="cog" aria-hidden="true"></my-global-icon> | ||
3 | <ng-container i18n>Two factor authentication</ng-container> | ||
4 | </h1> | ||
5 | |||
6 | <div i18n *ngIf="twoFactorAlreadyEnabled === true" class="root already-enabled"> | ||
7 | Two factor authentication is already enabled. | ||
8 | </div> | ||
9 | |||
10 | <div class="root" *ngIf="twoFactorAlreadyEnabled === false"> | ||
11 | <ng-container *ngIf="step === 'request'"> | ||
12 | <form role="form" (ngSubmit)="requestTwoFactor()" [formGroup]="formPassword"> | ||
13 | |||
14 | <label i18n for="current-password">Your password</label> | ||
15 | <div class="form-group-description" i18n>Confirm your password to enable two factor authentication</div> | ||
16 | |||
17 | <my-input-text | ||
18 | formControlName="current-password" inputId="current-password" i18n-placeholder placeholder="Current password" | ||
19 | [formError]="formErrorsPassword['current-password']" autocomplete="current-password" | ||
20 | ></my-input-text> | ||
21 | |||
22 | <input class="peertube-button orange-button mt-3" type="submit" i18n-value value="Confirm" [disabled]="!formPassword.valid"> | ||
23 | </form> | ||
24 | </ng-container> | ||
25 | |||
26 | <ng-container *ngIf="step === 'confirm'"> | ||
27 | |||
28 | <p i18n> | ||
29 | Scan this QR code into a TOTP app on your phone. This app will generate tokens that you will have to enter when logging in. | ||
30 | </p> | ||
31 | |||
32 | <qrcode [qrdata]="twoFactorURI" [width]="256" level="Q"></qrcode> | ||
33 | |||
34 | <div i18n> | ||
35 | If you can't scan the QR code and need to enter it manually, here is the plain-text secret: | ||
36 | </div> | ||
37 | |||
38 | <div class="secret-plain-text">{{ twoFactorSecret }}</div> | ||
39 | |||
40 | <form class="mt-3" role="form" (ngSubmit)="confirmTwoFactor()" [formGroup]="formOTP"> | ||
41 | |||
42 | <label i18n for="otp-token">Two-factor code</label> | ||
43 | <div class="form-group-description" i18n>Enter the code generated by your authenticator app to confirm</div> | ||
44 | |||
45 | <my-input-text | ||
46 | [show]="true" formControlName="otp-token" inputId="otp-token" | ||
47 | [formError]="formErrorsOTP['otp-token']" autocomplete="otp-token" | ||
48 | ></my-input-text> | ||
49 | |||
50 | <input class="peertube-button orange-button mt-3" type="submit" i18n-value value="Confirm" [disabled]="!formOTP.valid"> | ||
51 | </form> | ||
52 | </ng-container> | ||
53 | |||
54 | </div> | ||
diff --git a/client/src/app/+my-account/my-account-settings/my-account-two-factor/my-account-two-factor.component.scss b/client/src/app/+my-account/my-account-settings/my-account-two-factor/my-account-two-factor.component.scss new file mode 100644 index 000000000..cee016bb8 --- /dev/null +++ b/client/src/app/+my-account/my-account-settings/my-account-two-factor/my-account-two-factor.component.scss | |||
@@ -0,0 +1,16 @@ | |||
1 | @use '_variables' as *; | ||
2 | @use '_mixins' as *; | ||
3 | |||
4 | .root { | ||
5 | max-width: 600px; | ||
6 | } | ||
7 | |||
8 | .secret-plain-text { | ||
9 | font-family: monospace; | ||
10 | font-size: 0.9rem; | ||
11 | } | ||
12 | |||
13 | qrcode { | ||
14 | display: inline-block; | ||
15 | margin: auto; | ||
16 | } | ||
diff --git a/client/src/app/+my-account/my-account-settings/my-account-two-factor/my-account-two-factor.component.ts b/client/src/app/+my-account/my-account-settings/my-account-two-factor/my-account-two-factor.component.ts new file mode 100644 index 000000000..259090d64 --- /dev/null +++ b/client/src/app/+my-account/my-account-settings/my-account-two-factor/my-account-two-factor.component.ts | |||
@@ -0,0 +1,105 @@ | |||
1 | import { Component, OnInit } from '@angular/core' | ||
2 | import { FormGroup } from '@angular/forms' | ||
3 | import { Router } from '@angular/router' | ||
4 | import { AuthService, Notifier, User } from '@app/core' | ||
5 | import { USER_EXISTING_PASSWORD_VALIDATOR, USER_OTP_TOKEN_VALIDATOR } from '@app/shared/form-validators/user-validators' | ||
6 | import { FormReactiveService } from '@app/shared/shared-forms' | ||
7 | import { TwoFactorService } from '@app/shared/shared-users' | ||
8 | |||
9 | @Component({ | ||
10 | selector: 'my-account-two-factor', | ||
11 | templateUrl: './my-account-two-factor.component.html', | ||
12 | styleUrls: [ './my-account-two-factor.component.scss' ] | ||
13 | }) | ||
14 | export class MyAccountTwoFactorComponent implements OnInit { | ||
15 | twoFactorAlreadyEnabled: boolean | ||
16 | |||
17 | step: 'request' | 'confirm' | 'confirmed' = 'request' | ||
18 | |||
19 | twoFactorSecret: string | ||
20 | twoFactorURI: string | ||
21 | |||
22 | inPasswordStep = true | ||
23 | |||
24 | formPassword: FormGroup | ||
25 | formErrorsPassword: any | ||
26 | |||
27 | formOTP: FormGroup | ||
28 | formErrorsOTP: any | ||
29 | |||
30 | private user: User | ||
31 | private requestToken: string | ||
32 | |||
33 | constructor ( | ||
34 | private notifier: Notifier, | ||
35 | private twoFactorService: TwoFactorService, | ||
36 | private formReactiveService: FormReactiveService, | ||
37 | private auth: AuthService, | ||
38 | private router: Router | ||
39 | ) { | ||
40 | } | ||
41 | |||
42 | ngOnInit () { | ||
43 | this.buildPasswordForm() | ||
44 | this.buildOTPForm() | ||
45 | |||
46 | this.auth.userInformationLoaded.subscribe(() => { | ||
47 | this.user = this.auth.getUser() | ||
48 | |||
49 | this.twoFactorAlreadyEnabled = this.user.twoFactorEnabled | ||
50 | }) | ||
51 | } | ||
52 | |||
53 | requestTwoFactor () { | ||
54 | this.twoFactorService.requestTwoFactor({ | ||
55 | userId: this.user.id, | ||
56 | currentPassword: this.formPassword.value['current-password'] | ||
57 | }).subscribe({ | ||
58 | next: ({ otpRequest }) => { | ||
59 | this.requestToken = otpRequest.requestToken | ||
60 | this.twoFactorURI = otpRequest.uri | ||
61 | this.twoFactorSecret = otpRequest.secret.replace(/(.{4})/g, '$1 ').trim() | ||
62 | |||
63 | this.step = 'confirm' | ||
64 | }, | ||
65 | |||
66 | error: err => this.notifier.error(err.message) | ||
67 | }) | ||
68 | } | ||
69 | |||
70 | confirmTwoFactor () { | ||
71 | this.twoFactorService.confirmTwoFactorRequest({ | ||
72 | userId: this.user.id, | ||
73 | requestToken: this.requestToken, | ||
74 | otpToken: this.formOTP.value['otp-token'] | ||
75 | }).subscribe({ | ||
76 | next: () => { | ||
77 | this.notifier.success($localize`Two factor authentication has been enabled.`) | ||
78 | |||
79 | this.auth.refreshUserInformation() | ||
80 | |||
81 | this.router.navigateByUrl('/my-account/settings') | ||
82 | }, | ||
83 | |||
84 | error: err => this.notifier.error(err.message) | ||
85 | }) | ||
86 | } | ||
87 | |||
88 | private buildPasswordForm () { | ||
89 | const { form, formErrors } = this.formReactiveService.buildForm({ | ||
90 | 'current-password': USER_EXISTING_PASSWORD_VALIDATOR | ||
91 | }) | ||
92 | |||
93 | this.formPassword = form | ||
94 | this.formErrorsPassword = formErrors | ||
95 | } | ||
96 | |||
97 | private buildOTPForm () { | ||
98 | const { form, formErrors } = this.formReactiveService.buildForm({ | ||
99 | 'otp-token': USER_OTP_TOKEN_VALIDATOR | ||
100 | }) | ||
101 | |||
102 | this.formOTP = form | ||
103 | this.formErrorsOTP = formErrors | ||
104 | } | ||
105 | } | ||
diff --git a/client/src/app/+my-account/my-account.module.ts b/client/src/app/+my-account/my-account.module.ts index 4081e4f01..84b057647 100644 --- a/client/src/app/+my-account/my-account.module.ts +++ b/client/src/app/+my-account/my-account.module.ts | |||
@@ -1,3 +1,4 @@ | |||
1 | import { QRCodeModule } from 'angularx-qrcode' | ||
1 | import { AutoCompleteModule } from 'primeng/autocomplete' | 2 | import { AutoCompleteModule } from 'primeng/autocomplete' |
2 | import { TableModule } from 'primeng/table' | 3 | import { TableModule } from 'primeng/table' |
3 | import { DragDropModule } from '@angular/cdk/drag-drop' | 4 | import { DragDropModule } from '@angular/cdk/drag-drop' |
@@ -10,6 +11,7 @@ import { SharedMainModule } from '@app/shared/shared-main' | |||
10 | import { SharedModerationModule } from '@app/shared/shared-moderation' | 11 | import { SharedModerationModule } from '@app/shared/shared-moderation' |
11 | import { SharedShareModal } from '@app/shared/shared-share-modal' | 12 | import { SharedShareModal } from '@app/shared/shared-share-modal' |
12 | import { SharedUserInterfaceSettingsModule } from '@app/shared/shared-user-settings' | 13 | import { SharedUserInterfaceSettingsModule } from '@app/shared/shared-user-settings' |
14 | import { SharedUsersModule } from '@app/shared/shared-users' | ||
13 | import { SharedActorImageModule } from '../shared/shared-actor-image/shared-actor-image.module' | 15 | import { SharedActorImageModule } from '../shared/shared-actor-image/shared-actor-image.module' |
14 | import { MyAccountAbusesListComponent } from './my-account-abuses/my-account-abuses-list.component' | 16 | import { MyAccountAbusesListComponent } from './my-account-abuses/my-account-abuses-list.component' |
15 | import { MyAccountApplicationsComponent } from './my-account-applications/my-account-applications.component' | 17 | import { MyAccountApplicationsComponent } from './my-account-applications/my-account-applications.component' |
@@ -23,12 +25,14 @@ import { MyAccountDangerZoneComponent } from './my-account-settings/my-account-d | |||
23 | import { MyAccountNotificationPreferencesComponent } from './my-account-settings/my-account-notification-preferences' | 25 | import { MyAccountNotificationPreferencesComponent } from './my-account-settings/my-account-notification-preferences' |
24 | import { MyAccountProfileComponent } from './my-account-settings/my-account-profile/my-account-profile.component' | 26 | import { MyAccountProfileComponent } from './my-account-settings/my-account-profile/my-account-profile.component' |
25 | import { MyAccountSettingsComponent } from './my-account-settings/my-account-settings.component' | 27 | import { MyAccountSettingsComponent } from './my-account-settings/my-account-settings.component' |
28 | import { MyAccountTwoFactorButtonComponent, MyAccountTwoFactorComponent } from './my-account-settings/my-account-two-factor' | ||
26 | import { MyAccountComponent } from './my-account.component' | 29 | import { MyAccountComponent } from './my-account.component' |
27 | 30 | ||
28 | @NgModule({ | 31 | @NgModule({ |
29 | imports: [ | 32 | imports: [ |
30 | MyAccountRoutingModule, | 33 | MyAccountRoutingModule, |
31 | 34 | ||
35 | QRCodeModule, | ||
32 | AutoCompleteModule, | 36 | AutoCompleteModule, |
33 | TableModule, | 37 | TableModule, |
34 | DragDropModule, | 38 | DragDropModule, |
@@ -37,6 +41,7 @@ import { MyAccountComponent } from './my-account.component' | |||
37 | SharedFormModule, | 41 | SharedFormModule, |
38 | SharedModerationModule, | 42 | SharedModerationModule, |
39 | SharedUserInterfaceSettingsModule, | 43 | SharedUserInterfaceSettingsModule, |
44 | SharedUsersModule, | ||
40 | SharedGlobalIconModule, | 45 | SharedGlobalIconModule, |
41 | SharedAbuseListModule, | 46 | SharedAbuseListModule, |
42 | SharedShareModal, | 47 | SharedShareModal, |
@@ -52,6 +57,9 @@ import { MyAccountComponent } from './my-account.component' | |||
52 | MyAccountChangeEmailComponent, | 57 | MyAccountChangeEmailComponent, |
53 | MyAccountApplicationsComponent, | 58 | MyAccountApplicationsComponent, |
54 | 59 | ||
60 | MyAccountTwoFactorButtonComponent, | ||
61 | MyAccountTwoFactorComponent, | ||
62 | |||
55 | MyAccountDangerZoneComponent, | 63 | MyAccountDangerZoneComponent, |
56 | MyAccountBlocklistComponent, | 64 | MyAccountBlocklistComponent, |
57 | MyAccountAbusesListComponent, | 65 | MyAccountAbusesListComponent, |
diff --git a/client/src/app/+my-library/+my-video-channels/my-video-channels.component.ts b/client/src/app/+my-library/+my-video-channels/my-video-channels.component.ts index 205ad7a89..ece59c2ff 100644 --- a/client/src/app/+my-library/+my-video-channels/my-video-channels.component.ts +++ b/client/src/app/+my-library/+my-video-channels/my-video-channels.component.ts | |||
@@ -40,7 +40,7 @@ export class MyVideoChannelsComponent { | |||
40 | } | 40 | } |
41 | 41 | ||
42 | async deleteVideoChannel (videoChannel: VideoChannel) { | 42 | async deleteVideoChannel (videoChannel: VideoChannel) { |
43 | const res = await this.confirmService.confirmWithInput( | 43 | const res = await this.confirmService.confirmWithExpectedInput( |
44 | $localize`Do you really want to delete ${videoChannel.displayName}? | 44 | $localize`Do you really want to delete ${videoChannel.displayName}? |
45 | It will delete ${videoChannel.videosCount} videos uploaded in this channel, and you will not be able to create another | 45 | It will delete ${videoChannel.videosCount} videos uploaded in this channel, and you will not be able to create another |
46 | channel with the same name (${videoChannel.name})!`, | 46 | channel with the same name (${videoChannel.name})!`, |
diff --git a/client/src/app/+my-library/my-ownership/my-accept-ownership/my-accept-ownership.component.ts b/client/src/app/+my-library/my-ownership/my-accept-ownership/my-accept-ownership.component.ts index 8ead237c7..ca7eb680b 100644 --- a/client/src/app/+my-library/my-ownership/my-accept-ownership/my-accept-ownership.component.ts +++ b/client/src/app/+my-library/my-ownership/my-accept-ownership/my-accept-ownership.component.ts | |||
@@ -3,7 +3,7 @@ import { Component, ElementRef, EventEmitter, OnInit, Output, ViewChild } from ' | |||
3 | import { AuthService, Notifier } from '@app/core' | 3 | import { AuthService, Notifier } from '@app/core' |
4 | import { listUserChannelsForSelect } from '@app/helpers' | 4 | import { listUserChannelsForSelect } from '@app/helpers' |
5 | import { OWNERSHIP_CHANGE_CHANNEL_VALIDATOR } from '@app/shared/form-validators/video-ownership-change-validators' | 5 | import { OWNERSHIP_CHANGE_CHANNEL_VALIDATOR } from '@app/shared/form-validators/video-ownership-change-validators' |
6 | import { FormReactive, FormValidatorService } from '@app/shared/shared-forms' | 6 | import { FormReactive, FormReactiveService } from '@app/shared/shared-forms' |
7 | import { VideoOwnershipService } from '@app/shared/shared-main' | 7 | import { VideoOwnershipService } from '@app/shared/shared-main' |
8 | import { NgbModal } from '@ng-bootstrap/ng-bootstrap' | 8 | import { NgbModal } from '@ng-bootstrap/ng-bootstrap' |
9 | import { VideoChangeOwnership } from '@shared/models' | 9 | import { VideoChangeOwnership } from '@shared/models' |
@@ -24,7 +24,7 @@ export class MyAcceptOwnershipComponent extends FormReactive implements OnInit { | |||
24 | error: string = null | 24 | error: string = null |
25 | 25 | ||
26 | constructor ( | 26 | constructor ( |
27 | protected formValidatorService: FormValidatorService, | 27 | protected formReactiveService: FormReactiveService, |
28 | private videoOwnershipService: VideoOwnershipService, | 28 | private videoOwnershipService: VideoOwnershipService, |
29 | private notifier: Notifier, | 29 | private notifier: Notifier, |
30 | private authService: AuthService, | 30 | private authService: AuthService, |
diff --git a/client/src/app/+my-library/my-video-channel-syncs/video-channel-sync-edit/video-channel-sync-edit.component.ts b/client/src/app/+my-library/my-video-channel-syncs/video-channel-sync-edit/video-channel-sync-edit.component.ts index 9ceb6dfd1..a14ab5b92 100644 --- a/client/src/app/+my-library/my-video-channel-syncs/video-channel-sync-edit/video-channel-sync-edit.component.ts +++ b/client/src/app/+my-library/my-video-channel-syncs/video-channel-sync-edit/video-channel-sync-edit.component.ts | |||
@@ -5,7 +5,7 @@ import { Router } from '@angular/router' | |||
5 | import { AuthService, Notifier } from '@app/core' | 5 | import { AuthService, Notifier } from '@app/core' |
6 | import { listUserChannelsForSelect } from '@app/helpers' | 6 | import { listUserChannelsForSelect } from '@app/helpers' |
7 | import { VIDEO_CHANNEL_EXTERNAL_URL_VALIDATOR } from '@app/shared/form-validators/video-channel-validators' | 7 | import { VIDEO_CHANNEL_EXTERNAL_URL_VALIDATOR } from '@app/shared/form-validators/video-channel-validators' |
8 | import { FormReactive, FormValidatorService } from '@app/shared/shared-forms' | 8 | import { FormReactive, FormReactiveService } from '@app/shared/shared-forms' |
9 | import { VideoChannelService, VideoChannelSyncService } from '@app/shared/shared-main' | 9 | import { VideoChannelService, VideoChannelSyncService } from '@app/shared/shared-main' |
10 | import { VideoChannelSyncCreate } from '@shared/models/videos' | 10 | import { VideoChannelSyncCreate } from '@shared/models/videos' |
11 | 11 | ||
@@ -20,7 +20,7 @@ export class VideoChannelSyncEditComponent extends FormReactive implements OnIni | |||
20 | existingVideosStrategy: string | 20 | existingVideosStrategy: string |
21 | 21 | ||
22 | constructor ( | 22 | constructor ( |
23 | protected formValidatorService: FormValidatorService, | 23 | protected formReactiveService: FormReactiveService, |
24 | private authService: AuthService, | 24 | private authService: AuthService, |
25 | private router: Router, | 25 | private router: Router, |
26 | private notifier: Notifier, | 26 | private notifier: Notifier, |
diff --git a/client/src/app/+my-library/my-video-playlists/my-video-playlist-create.component.ts b/client/src/app/+my-library/my-video-playlists/my-video-playlist-create.component.ts index 9eb3e9888..63f72df3f 100644 --- a/client/src/app/+my-library/my-video-playlists/my-video-playlist-create.component.ts +++ b/client/src/app/+my-library/my-video-playlists/my-video-playlist-create.component.ts | |||
@@ -9,7 +9,7 @@ import { | |||
9 | VIDEO_PLAYLIST_DISPLAY_NAME_VALIDATOR, | 9 | VIDEO_PLAYLIST_DISPLAY_NAME_VALIDATOR, |
10 | VIDEO_PLAYLIST_PRIVACY_VALIDATOR | 10 | VIDEO_PLAYLIST_PRIVACY_VALIDATOR |
11 | } from '@app/shared/form-validators/video-playlist-validators' | 11 | } from '@app/shared/form-validators/video-playlist-validators' |
12 | import { FormValidatorService } from '@app/shared/shared-forms' | 12 | import { FormReactiveService } from '@app/shared/shared-forms' |
13 | import { VideoPlaylistService } from '@app/shared/shared-video-playlist' | 13 | import { VideoPlaylistService } from '@app/shared/shared-video-playlist' |
14 | import { VideoPlaylistCreate } from '@shared/models/videos/playlist/video-playlist-create.model' | 14 | import { VideoPlaylistCreate } from '@shared/models/videos/playlist/video-playlist-create.model' |
15 | import { VideoPlaylistPrivacy } from '@shared/models/videos/playlist/video-playlist-privacy.model' | 15 | import { VideoPlaylistPrivacy } from '@shared/models/videos/playlist/video-playlist-privacy.model' |
@@ -23,7 +23,7 @@ export class MyVideoPlaylistCreateComponent extends MyVideoPlaylistEdit implemen | |||
23 | error: string | 23 | error: string |
24 | 24 | ||
25 | constructor ( | 25 | constructor ( |
26 | protected formValidatorService: FormValidatorService, | 26 | protected formReactiveService: FormReactiveService, |
27 | private authService: AuthService, | 27 | private authService: AuthService, |
28 | private notifier: Notifier, | 28 | private notifier: Notifier, |
29 | private router: Router, | 29 | private router: Router, |
diff --git a/client/src/app/+my-library/my-video-playlists/my-video-playlist-update.component.ts b/client/src/app/+my-library/my-video-playlists/my-video-playlist-update.component.ts index ef7ba0018..bbe8a5f80 100644 --- a/client/src/app/+my-library/my-video-playlists/my-video-playlist-update.component.ts +++ b/client/src/app/+my-library/my-video-playlists/my-video-playlist-update.component.ts | |||
@@ -11,7 +11,7 @@ import { | |||
11 | VIDEO_PLAYLIST_DISPLAY_NAME_VALIDATOR, | 11 | VIDEO_PLAYLIST_DISPLAY_NAME_VALIDATOR, |
12 | VIDEO_PLAYLIST_PRIVACY_VALIDATOR | 12 | VIDEO_PLAYLIST_PRIVACY_VALIDATOR |
13 | } from '@app/shared/form-validators/video-playlist-validators' | 13 | } from '@app/shared/form-validators/video-playlist-validators' |
14 | import { FormValidatorService } from '@app/shared/shared-forms' | 14 | import { FormReactiveService } from '@app/shared/shared-forms' |
15 | import { VideoPlaylist, VideoPlaylistService } from '@app/shared/shared-video-playlist' | 15 | import { VideoPlaylist, VideoPlaylistService } from '@app/shared/shared-video-playlist' |
16 | import { VideoPlaylistUpdate } from '@shared/models' | 16 | import { VideoPlaylistUpdate } from '@shared/models' |
17 | import { MyVideoPlaylistEdit } from './my-video-playlist-edit' | 17 | import { MyVideoPlaylistEdit } from './my-video-playlist-edit' |
@@ -27,7 +27,7 @@ export class MyVideoPlaylistUpdateComponent extends MyVideoPlaylistEdit implemen | |||
27 | private paramsSub: Subscription | 27 | private paramsSub: Subscription |
28 | 28 | ||
29 | constructor ( | 29 | constructor ( |
30 | protected formValidatorService: FormValidatorService, | 30 | protected formReactiveService: FormReactiveService, |
31 | private authService: AuthService, | 31 | private authService: AuthService, |
32 | private notifier: Notifier, | 32 | private notifier: Notifier, |
33 | private router: Router, | 33 | private router: Router, |
diff --git a/client/src/app/+my-library/my-videos/modals/video-change-ownership.component.ts b/client/src/app/+my-library/my-videos/modals/video-change-ownership.component.ts index 960c9a4f7..72187e893 100644 --- a/client/src/app/+my-library/my-videos/modals/video-change-ownership.component.ts +++ b/client/src/app/+my-library/my-videos/modals/video-change-ownership.component.ts | |||
@@ -1,7 +1,7 @@ | |||
1 | import { Component, ElementRef, OnInit, ViewChild } from '@angular/core' | 1 | import { Component, ElementRef, OnInit, ViewChild } from '@angular/core' |
2 | import { Notifier, UserService } from '@app/core' | 2 | import { Notifier, UserService } from '@app/core' |
3 | import { OWNERSHIP_CHANGE_USERNAME_VALIDATOR } from '@app/shared/form-validators/video-ownership-change-validators' | 3 | import { OWNERSHIP_CHANGE_USERNAME_VALIDATOR } from '@app/shared/form-validators/video-ownership-change-validators' |
4 | import { FormReactive, FormValidatorService } from '@app/shared/shared-forms' | 4 | import { FormReactive, FormReactiveService } from '@app/shared/shared-forms' |
5 | import { Video, VideoOwnershipService } from '@app/shared/shared-main' | 5 | import { Video, VideoOwnershipService } from '@app/shared/shared-main' |
6 | import { NgbModal } from '@ng-bootstrap/ng-bootstrap' | 6 | import { NgbModal } from '@ng-bootstrap/ng-bootstrap' |
7 | 7 | ||
@@ -20,7 +20,7 @@ export class VideoChangeOwnershipComponent extends FormReactive implements OnIni | |||
20 | private video: Video | undefined = undefined | 20 | private video: Video | undefined = undefined |
21 | 21 | ||
22 | constructor ( | 22 | constructor ( |
23 | protected formValidatorService: FormValidatorService, | 23 | protected formReactiveService: FormReactiveService, |
24 | private videoOwnershipService: VideoOwnershipService, | 24 | private videoOwnershipService: VideoOwnershipService, |
25 | private notifier: Notifier, | 25 | private notifier: Notifier, |
26 | private userService: UserService, | 26 | private userService: UserService, |
diff --git a/client/src/app/+reset-password/reset-password.component.ts b/client/src/app/+reset-password/reset-password.component.ts index 11c5110fd..44216f978 100644 --- a/client/src/app/+reset-password/reset-password.component.ts +++ b/client/src/app/+reset-password/reset-password.component.ts | |||
@@ -3,7 +3,7 @@ import { ActivatedRoute, Router } from '@angular/router' | |||
3 | import { Notifier, UserService } from '@app/core' | 3 | import { Notifier, UserService } from '@app/core' |
4 | import { RESET_PASSWORD_CONFIRM_VALIDATOR } from '@app/shared/form-validators/reset-password-validators' | 4 | import { RESET_PASSWORD_CONFIRM_VALIDATOR } from '@app/shared/form-validators/reset-password-validators' |
5 | import { USER_PASSWORD_VALIDATOR } from '@app/shared/form-validators/user-validators' | 5 | import { USER_PASSWORD_VALIDATOR } from '@app/shared/form-validators/user-validators' |
6 | import { FormReactive, FormValidatorService } from '@app/shared/shared-forms' | 6 | import { FormReactive, FormReactiveService } from '@app/shared/shared-forms' |
7 | 7 | ||
8 | @Component({ | 8 | @Component({ |
9 | selector: 'my-login', | 9 | selector: 'my-login', |
@@ -16,7 +16,7 @@ export class ResetPasswordComponent extends FormReactive implements OnInit { | |||
16 | private verificationString: string | 16 | private verificationString: string |
17 | 17 | ||
18 | constructor ( | 18 | constructor ( |
19 | protected formValidatorService: FormValidatorService, | 19 | protected formReactiveService: FormReactiveService, |
20 | private userService: UserService, | 20 | private userService: UserService, |
21 | private notifier: Notifier, | 21 | private notifier: Notifier, |
22 | private router: Router, | 22 | private router: Router, |
diff --git a/client/src/app/+signup/+register/register.component.ts b/client/src/app/+signup/+register/register.component.ts index 4ab327b1b..958770ebf 100644 --- a/client/src/app/+signup/+register/register.component.ts +++ b/client/src/app/+signup/+register/register.component.ts | |||
@@ -158,7 +158,7 @@ export class RegisterComponent implements OnInit { | |||
158 | } | 158 | } |
159 | 159 | ||
160 | // Auto login | 160 | // Auto login |
161 | this.authService.login(body.username, body.password) | 161 | this.authService.login({ username: body.username, password: body.password }) |
162 | .subscribe({ | 162 | .subscribe({ |
163 | next: () => { | 163 | next: () => { |
164 | this.signupSuccess = true | 164 | this.signupSuccess = true |
diff --git a/client/src/app/+signup/+register/steps/register-step-channel.component.ts b/client/src/app/+signup/+register/steps/register-step-channel.component.ts index c10b568ba..df92c5145 100644 --- a/client/src/app/+signup/+register/steps/register-step-channel.component.ts +++ b/client/src/app/+signup/+register/steps/register-step-channel.component.ts | |||
@@ -3,7 +3,7 @@ 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 { 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' |
6 | import { FormReactive, FormValidatorService } from '@app/shared/shared-forms' | 6 | import { FormReactive, FormReactiveService } from '@app/shared/shared-forms' |
7 | import { UserSignupService } from '@app/shared/shared-users' | 7 | import { UserSignupService } from '@app/shared/shared-users' |
8 | 8 | ||
9 | @Component({ | 9 | @Component({ |
@@ -19,7 +19,7 @@ export class RegisterStepChannelComponent extends FormReactive implements OnInit | |||
19 | @Output() formBuilt = new EventEmitter<FormGroup>() | 19 | @Output() formBuilt = new EventEmitter<FormGroup>() |
20 | 20 | ||
21 | constructor ( | 21 | constructor ( |
22 | protected formValidatorService: FormValidatorService, | 22 | protected formReactiveService: FormReactiveService, |
23 | private userSignupService: UserSignupService | 23 | private userSignupService: UserSignupService |
24 | ) { | 24 | ) { |
25 | super() | 25 | super() |
diff --git a/client/src/app/+signup/+register/steps/register-step-terms.component.ts b/client/src/app/+signup/+register/steps/register-step-terms.component.ts index 87d16696e..2df963b30 100644 --- a/client/src/app/+signup/+register/steps/register-step-terms.component.ts +++ b/client/src/app/+signup/+register/steps/register-step-terms.component.ts | |||
@@ -1,9 +1,7 @@ | |||
1 | import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core' | 1 | import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core' |
2 | import { FormGroup } from '@angular/forms' | 2 | import { FormGroup } from '@angular/forms' |
3 | import { | 3 | import { USER_TERMS_VALIDATOR } from '@app/shared/form-validators/user-validators' |
4 | USER_TERMS_VALIDATOR | 4 | import { FormReactive, FormReactiveService } from '@app/shared/shared-forms' |
5 | } from '@app/shared/form-validators/user-validators' | ||
6 | import { FormReactive, FormValidatorService } from '@app/shared/shared-forms' | ||
7 | 5 | ||
8 | @Component({ | 6 | @Component({ |
9 | selector: 'my-register-step-terms', | 7 | selector: 'my-register-step-terms', |
@@ -19,7 +17,7 @@ export class RegisterStepTermsComponent extends FormReactive implements OnInit { | |||
19 | @Output() codeOfConductClick = new EventEmitter<void>() | 17 | @Output() codeOfConductClick = new EventEmitter<void>() |
20 | 18 | ||
21 | constructor ( | 19 | constructor ( |
22 | protected formValidatorService: FormValidatorService | 20 | protected formReactiveService: FormReactiveService |
23 | ) { | 21 | ) { |
24 | super() | 22 | super() |
25 | } | 23 | } |
diff --git a/client/src/app/+signup/+register/steps/register-step-user.component.ts b/client/src/app/+signup/+register/steps/register-step-user.component.ts index b89e38a28..822f8f5c5 100644 --- a/client/src/app/+signup/+register/steps/register-step-user.component.ts +++ b/client/src/app/+signup/+register/steps/register-step-user.component.ts | |||
@@ -8,7 +8,7 @@ import { | |||
8 | USER_PASSWORD_VALIDATOR, | 8 | USER_PASSWORD_VALIDATOR, |
9 | USER_USERNAME_VALIDATOR | 9 | USER_USERNAME_VALIDATOR |
10 | } from '@app/shared/form-validators/user-validators' | 10 | } from '@app/shared/form-validators/user-validators' |
11 | import { FormReactive, FormValidatorService } from '@app/shared/shared-forms' | 11 | import { FormReactive, FormReactiveService } from '@app/shared/shared-forms' |
12 | import { UserSignupService } from '@app/shared/shared-users' | 12 | import { UserSignupService } from '@app/shared/shared-users' |
13 | 13 | ||
14 | @Component({ | 14 | @Component({ |
@@ -23,7 +23,7 @@ export class RegisterStepUserComponent extends FormReactive implements OnInit { | |||
23 | @Output() formBuilt = new EventEmitter<FormGroup>() | 23 | @Output() formBuilt = new EventEmitter<FormGroup>() |
24 | 24 | ||
25 | constructor ( | 25 | constructor ( |
26 | protected formValidatorService: FormValidatorService, | 26 | protected formReactiveService: FormReactiveService, |
27 | private userSignupService: UserSignupService | 27 | private userSignupService: UserSignupService |
28 | ) { | 28 | ) { |
29 | super() | 29 | super() |
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 a0ed66a3a..06905f678 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,7 @@ | |||
1 | import { Component, OnInit } from '@angular/core' | 1 | import { Component, OnInit } from '@angular/core' |
2 | import { Notifier, RedirectService, ServerService } 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, FormReactiveService } from '@app/shared/shared-forms' |
5 | import { UserSignupService } from '@app/shared/shared-users' | 5 | import { UserSignupService } from '@app/shared/shared-users' |
6 | 6 | ||
7 | @Component({ | 7 | @Component({ |
@@ -14,7 +14,7 @@ export class VerifyAccountAskSendEmailComponent extends FormReactive implements | |||
14 | requiresEmailVerification = false | 14 | requiresEmailVerification = false |
15 | 15 | ||
16 | constructor ( | 16 | constructor ( |
17 | protected formValidatorService: FormValidatorService, | 17 | protected formReactiveService: FormReactiveService, |
18 | private userSignupService: UserSignupService, | 18 | private userSignupService: UserSignupService, |
19 | private serverService: ServerService, | 19 | private serverService: ServerService, |
20 | private notifier: Notifier, | 20 | private notifier: Notifier, |
diff --git a/client/src/app/+video-studio/edit/video-studio-edit.component.ts b/client/src/app/+video-studio/edit/video-studio-edit.component.ts index bf91c237a..dad083bf9 100644 --- a/client/src/app/+video-studio/edit/video-studio-edit.component.ts +++ b/client/src/app/+video-studio/edit/video-studio-edit.component.ts | |||
@@ -1,7 +1,7 @@ | |||
1 | import { Component, OnInit } from '@angular/core' | 1 | import { Component, OnInit } from '@angular/core' |
2 | import { ActivatedRoute, Router } from '@angular/router' | 2 | import { ActivatedRoute, Router } from '@angular/router' |
3 | import { ConfirmService, Notifier, ServerService } from '@app/core' | 3 | import { ConfirmService, Notifier, ServerService } from '@app/core' |
4 | import { FormReactive, FormValidatorService } from '@app/shared/shared-forms' | 4 | import { FormReactive, FormReactiveService } from '@app/shared/shared-forms' |
5 | import { VideoDetails } from '@app/shared/shared-main' | 5 | import { VideoDetails } from '@app/shared/shared-main' |
6 | import { LoadingBarService } from '@ngx-loading-bar/core' | 6 | import { LoadingBarService } from '@ngx-loading-bar/core' |
7 | import { logger } from '@root-helpers/logger' | 7 | import { logger } from '@root-helpers/logger' |
@@ -20,7 +20,7 @@ export class VideoStudioEditComponent extends FormReactive implements OnInit { | |||
20 | video: VideoDetails | 20 | video: VideoDetails |
21 | 21 | ||
22 | constructor ( | 22 | constructor ( |
23 | protected formValidatorService: FormValidatorService, | 23 | protected formReactiveService: FormReactiveService, |
24 | private serverService: ServerService, | 24 | private serverService: ServerService, |
25 | private notifier: Notifier, | 25 | private notifier: Notifier, |
26 | private router: Router, | 26 | private router: Router, |
diff --git a/client/src/app/+videos/+video-edit/shared/video-caption-add-modal.component.ts b/client/src/app/+videos/+video-edit/shared/video-caption-add-modal.component.ts index 95d83b131..4ab2d42db 100644 --- a/client/src/app/+videos/+video-edit/shared/video-caption-add-modal.component.ts +++ b/client/src/app/+videos/+video-edit/shared/video-caption-add-modal.component.ts | |||
@@ -1,7 +1,7 @@ | |||
1 | import { Component, ElementRef, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core' | 1 | import { Component, ElementRef, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core' |
2 | import { ServerService } from '@app/core' | 2 | import { ServerService } from '@app/core' |
3 | import { VIDEO_CAPTION_FILE_VALIDATOR, VIDEO_CAPTION_LANGUAGE_VALIDATOR } from '@app/shared/form-validators/video-captions-validators' | 3 | import { VIDEO_CAPTION_FILE_VALIDATOR, VIDEO_CAPTION_LANGUAGE_VALIDATOR } from '@app/shared/form-validators/video-captions-validators' |
4 | import { FormReactive, FormValidatorService } from '@app/shared/shared-forms' | 4 | import { FormReactive, FormReactiveService } from '@app/shared/shared-forms' |
5 | import { VideoCaptionEdit } from '@app/shared/shared-main' | 5 | import { VideoCaptionEdit } from '@app/shared/shared-main' |
6 | import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap' | 6 | import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap' |
7 | import { HTMLServerConfig, VideoConstant } from '@shared/models' | 7 | import { HTMLServerConfig, VideoConstant } from '@shared/models' |
@@ -26,7 +26,7 @@ export class VideoCaptionAddModalComponent extends FormReactive implements OnIni | |||
26 | private closingModal = false | 26 | private closingModal = false |
27 | 27 | ||
28 | constructor ( | 28 | constructor ( |
29 | protected formValidatorService: FormValidatorService, | 29 | protected formReactiveService: FormReactiveService, |
30 | private modalService: NgbModal, | 30 | private modalService: NgbModal, |
31 | private serverService: ServerService | 31 | private serverService: ServerService |
32 | ) { | 32 | ) { |
diff --git a/client/src/app/+videos/+video-edit/shared/video-caption-edit-modal-content/video-caption-edit-modal-content.component.ts b/client/src/app/+videos/+video-edit/shared/video-caption-edit-modal-content/video-caption-edit-modal-content.component.ts index f33353d36..2cb470a24 100644 --- a/client/src/app/+videos/+video-edit/shared/video-caption-edit-modal-content/video-caption-edit-modal-content.component.ts +++ b/client/src/app/+videos/+video-edit/shared/video-caption-edit-modal-content/video-caption-edit-modal-content.component.ts | |||
@@ -1,8 +1,8 @@ | |||
1 | import { Component, ElementRef, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core' | 1 | import { Component, ElementRef, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core' |
2 | import { VIDEO_CAPTION_FILE_CONTENT_VALIDATOR } from '@app/shared/form-validators/video-captions-validators' | 2 | import { VIDEO_CAPTION_FILE_CONTENT_VALIDATOR } from '@app/shared/form-validators/video-captions-validators' |
3 | import { FormReactive, FormValidatorService } from '@app/shared/shared-forms' | 3 | import { FormReactive, FormReactiveService } from '@app/shared/shared-forms' |
4 | import { VideoCaptionEdit, VideoCaptionService, VideoCaptionWithPathEdit } from '@app/shared/shared-main' | 4 | import { VideoCaptionEdit, VideoCaptionService, VideoCaptionWithPathEdit } from '@app/shared/shared-main' |
5 | import { NgbModal, NgbActiveModal } from '@ng-bootstrap/ng-bootstrap' | 5 | import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap' |
6 | import { HTMLServerConfig, VideoConstant } from '@shared/models' | 6 | import { HTMLServerConfig, VideoConstant } from '@shared/models' |
7 | import { ServerService } from '../../../../core' | 7 | import { ServerService } from '../../../../core' |
8 | 8 | ||
@@ -29,8 +29,7 @@ export class VideoCaptionEditModalContentComponent extends FormReactive implemen | |||
29 | 29 | ||
30 | constructor ( | 30 | constructor ( |
31 | protected openedModal: NgbActiveModal, | 31 | protected openedModal: NgbActiveModal, |
32 | protected formValidatorService: FormValidatorService, | 32 | protected formReactiveService: FormReactiveService, |
33 | private modalService: NgbModal, | ||
34 | private videoCaptionService: VideoCaptionService, | 33 | private videoCaptionService: VideoCaptionService, |
35 | private serverService: ServerService | 34 | private serverService: ServerService |
36 | ) { | 35 | ) { |
diff --git a/client/src/app/+videos/+video-edit/video-add-components/video-go-live.component.ts b/client/src/app/+videos/+video-edit/video-add-components/video-go-live.component.ts index 344b99ea2..4f2276e8c 100644 --- a/client/src/app/+videos/+video-edit/video-add-components/video-go-live.component.ts +++ b/client/src/app/+videos/+video-edit/video-add-components/video-go-live.component.ts | |||
@@ -3,7 +3,7 @@ import { AfterViewInit, Component, EventEmitter, OnInit, Output } from '@angular | |||
3 | import { Router } from '@angular/router' | 3 | import { Router } from '@angular/router' |
4 | import { AuthService, CanComponentDeactivate, HooksService, Notifier, ServerService } from '@app/core' | 4 | import { AuthService, CanComponentDeactivate, HooksService, Notifier, ServerService } from '@app/core' |
5 | import { scrollToTop } from '@app/helpers' | 5 | import { scrollToTop } from '@app/helpers' |
6 | import { FormValidatorService } from '@app/shared/shared-forms' | 6 | import { FormReactiveService } from '@app/shared/shared-forms' |
7 | import { Video, VideoCaptionService, VideoEdit, VideoService } from '@app/shared/shared-main' | 7 | import { Video, VideoCaptionService, VideoEdit, VideoService } from '@app/shared/shared-main' |
8 | import { LiveVideoService } from '@app/shared/shared-video-live' | 8 | import { LiveVideoService } from '@app/shared/shared-video-live' |
9 | import { LoadingBarService } from '@ngx-loading-bar/core' | 9 | import { LoadingBarService } from '@ngx-loading-bar/core' |
@@ -39,7 +39,7 @@ export class VideoGoLiveComponent extends VideoSend implements OnInit, AfterView | |||
39 | error: string | 39 | error: string |
40 | 40 | ||
41 | constructor ( | 41 | constructor ( |
42 | protected formValidatorService: FormValidatorService, | 42 | protected formReactiveService: FormReactiveService, |
43 | protected loadingBar: LoadingBarService, | 43 | protected loadingBar: LoadingBarService, |
44 | protected notifier: Notifier, | 44 | protected notifier: Notifier, |
45 | protected authService: AuthService, | 45 | protected authService: AuthService, |
diff --git a/client/src/app/+videos/+video-edit/video-add-components/video-import-torrent.component.ts b/client/src/app/+videos/+video-edit/video-add-components/video-import-torrent.component.ts index 7b9531d27..4a1408a4a 100644 --- a/client/src/app/+videos/+video-edit/video-add-components/video-import-torrent.component.ts +++ b/client/src/app/+videos/+video-edit/video-add-components/video-import-torrent.component.ts | |||
@@ -3,7 +3,7 @@ import { AfterViewInit, Component, ElementRef, EventEmitter, OnInit, Output, Vie | |||
3 | import { Router } from '@angular/router' | 3 | import { Router } from '@angular/router' |
4 | import { AuthService, CanComponentDeactivate, HooksService, Notifier, ServerService } from '@app/core' | 4 | import { AuthService, CanComponentDeactivate, HooksService, Notifier, ServerService } from '@app/core' |
5 | import { scrollToTop } from '@app/helpers' | 5 | import { scrollToTop } from '@app/helpers' |
6 | import { FormValidatorService } from '@app/shared/shared-forms' | 6 | import { FormReactiveService } from '@app/shared/shared-forms' |
7 | import { VideoCaptionService, VideoEdit, VideoImportService, VideoService } from '@app/shared/shared-main' | 7 | import { VideoCaptionService, VideoEdit, VideoImportService, VideoService } from '@app/shared/shared-main' |
8 | import { LoadingBarService } from '@ngx-loading-bar/core' | 8 | import { LoadingBarService } from '@ngx-loading-bar/core' |
9 | import { logger } from '@root-helpers/logger' | 9 | import { logger } from '@root-helpers/logger' |
@@ -35,7 +35,7 @@ export class VideoImportTorrentComponent extends VideoSend implements OnInit, Af | |||
35 | error: string | 35 | error: string |
36 | 36 | ||
37 | constructor ( | 37 | constructor ( |
38 | protected formValidatorService: FormValidatorService, | 38 | protected formReactiveService: FormReactiveService, |
39 | protected loadingBar: LoadingBarService, | 39 | protected loadingBar: LoadingBarService, |
40 | protected notifier: Notifier, | 40 | protected notifier: Notifier, |
41 | protected authService: AuthService, | 41 | protected authService: AuthService, |
diff --git a/client/src/app/+videos/+video-edit/video-add-components/video-import-url.component.ts b/client/src/app/+videos/+video-edit/video-add-components/video-import-url.component.ts index 422f0c643..502f3818e 100644 --- a/client/src/app/+videos/+video-edit/video-add-components/video-import-url.component.ts +++ b/client/src/app/+videos/+video-edit/video-add-components/video-import-url.component.ts | |||
@@ -4,7 +4,7 @@ import { AfterViewInit, Component, EventEmitter, OnInit, Output } from '@angular | |||
4 | import { Router } from '@angular/router' | 4 | import { Router } from '@angular/router' |
5 | import { AuthService, CanComponentDeactivate, HooksService, Notifier, ServerService } from '@app/core' | 5 | import { AuthService, CanComponentDeactivate, HooksService, Notifier, ServerService } from '@app/core' |
6 | import { scrollToTop } from '@app/helpers' | 6 | import { scrollToTop } from '@app/helpers' |
7 | import { FormValidatorService } from '@app/shared/shared-forms' | 7 | import { FormReactiveService } from '@app/shared/shared-forms' |
8 | import { VideoCaptionService, VideoEdit, VideoImportService, VideoService } from '@app/shared/shared-main' | 8 | import { VideoCaptionService, VideoEdit, VideoImportService, VideoService } from '@app/shared/shared-main' |
9 | import { LoadingBarService } from '@ngx-loading-bar/core' | 9 | import { LoadingBarService } from '@ngx-loading-bar/core' |
10 | import { logger } from '@root-helpers/logger' | 10 | import { logger } from '@root-helpers/logger' |
@@ -34,7 +34,7 @@ export class VideoImportUrlComponent extends VideoSend implements OnInit, AfterV | |||
34 | error: string | 34 | error: string |
35 | 35 | ||
36 | constructor ( | 36 | constructor ( |
37 | protected formValidatorService: FormValidatorService, | 37 | protected formReactiveService: FormReactiveService, |
38 | protected loadingBar: LoadingBarService, | 38 | protected loadingBar: LoadingBarService, |
39 | protected notifier: Notifier, | 39 | protected notifier: Notifier, |
40 | protected authService: AuthService, | 40 | protected authService: AuthService, |
diff --git a/client/src/app/+videos/+video-edit/video-add-components/video-upload.component.ts b/client/src/app/+videos/+video-edit/video-add-components/video-upload.component.ts index 19fba2a83..b0d846664 100644 --- a/client/src/app/+videos/+video-edit/video-add-components/video-upload.component.ts +++ b/client/src/app/+videos/+video-edit/video-add-components/video-upload.component.ts | |||
@@ -5,7 +5,7 @@ import { AfterViewInit, Component, ElementRef, EventEmitter, OnDestroy, OnInit, | |||
5 | import { ActivatedRoute, Router } from '@angular/router' | 5 | import { ActivatedRoute, Router } from '@angular/router' |
6 | import { AuthService, CanComponentDeactivate, HooksService, MetaService, Notifier, ServerService, UserService } from '@app/core' | 6 | import { AuthService, CanComponentDeactivate, HooksService, MetaService, Notifier, ServerService, UserService } from '@app/core' |
7 | import { genericUploadErrorHandler, scrollToTop } from '@app/helpers' | 7 | import { genericUploadErrorHandler, scrollToTop } from '@app/helpers' |
8 | import { FormValidatorService } from '@app/shared/shared-forms' | 8 | import { FormReactiveService } from '@app/shared/shared-forms' |
9 | import { BytesPipe, Video, VideoCaptionService, VideoEdit, VideoService } from '@app/shared/shared-main' | 9 | import { BytesPipe, Video, VideoCaptionService, VideoEdit, VideoService } from '@app/shared/shared-main' |
10 | import { LoadingBarService } from '@ngx-loading-bar/core' | 10 | import { LoadingBarService } from '@ngx-loading-bar/core' |
11 | import { logger } from '@root-helpers/logger' | 11 | import { logger } from '@root-helpers/logger' |
@@ -60,7 +60,7 @@ export class VideoUploadComponent extends VideoSend implements OnInit, OnDestroy | |||
60 | private uploadServiceSubscription: Subscription | 60 | private uploadServiceSubscription: Subscription |
61 | 61 | ||
62 | constructor ( | 62 | constructor ( |
63 | protected formValidatorService: FormValidatorService, | 63 | protected formReactiveService: FormReactiveService, |
64 | protected loadingBar: LoadingBarService, | 64 | protected loadingBar: LoadingBarService, |
65 | protected notifier: Notifier, | 65 | protected notifier: Notifier, |
66 | protected authService: AuthService, | 66 | protected authService: AuthService, |
diff --git a/client/src/app/+videos/+video-edit/video-update.component.ts b/client/src/app/+videos/+video-edit/video-update.component.ts index ed17dff06..212971447 100644 --- a/client/src/app/+videos/+video-edit/video-update.component.ts +++ b/client/src/app/+videos/+video-edit/video-update.component.ts | |||
@@ -4,7 +4,7 @@ import { SelectChannelItem } from 'src/types/select-options-item.model' | |||
4 | import { Component, HostListener, OnInit } from '@angular/core' | 4 | import { Component, HostListener, OnInit } from '@angular/core' |
5 | import { ActivatedRoute, Router } from '@angular/router' | 5 | import { ActivatedRoute, Router } from '@angular/router' |
6 | import { Notifier } from '@app/core' | 6 | import { Notifier } from '@app/core' |
7 | import { FormReactive, FormValidatorService } from '@app/shared/shared-forms' | 7 | import { FormReactive, FormReactiveService } from '@app/shared/shared-forms' |
8 | import { Video, VideoCaptionEdit, VideoCaptionService, VideoDetails, VideoEdit, VideoService } from '@app/shared/shared-main' | 8 | import { Video, VideoCaptionEdit, VideoCaptionService, VideoDetails, VideoEdit, VideoService } from '@app/shared/shared-main' |
9 | import { LiveVideoService } from '@app/shared/shared-video-live' | 9 | import { LiveVideoService } from '@app/shared/shared-video-live' |
10 | import { LoadingBarService } from '@ngx-loading-bar/core' | 10 | import { LoadingBarService } from '@ngx-loading-bar/core' |
@@ -33,7 +33,7 @@ export class VideoUpdateComponent extends FormReactive implements OnInit { | |||
33 | private updateDone = false | 33 | private updateDone = false |
34 | 34 | ||
35 | constructor ( | 35 | constructor ( |
36 | protected formValidatorService: FormValidatorService, | 36 | protected formReactiveService: FormReactiveService, |
37 | private route: ActivatedRoute, | 37 | private route: ActivatedRoute, |
38 | private router: Router, | 38 | private router: Router, |
39 | private notifier: Notifier, | 39 | private notifier: Notifier, |
diff --git a/client/src/app/+videos/+video-watch/shared/comment/video-comment-add.component.ts b/client/src/app/+videos/+video-watch/shared/comment/video-comment-add.component.ts index 9f4a68736..9a9bfe710 100644 --- a/client/src/app/+videos/+video-watch/shared/comment/video-comment-add.component.ts +++ b/client/src/app/+videos/+video-watch/shared/comment/video-comment-add.component.ts | |||
@@ -16,7 +16,7 @@ import { | |||
16 | import { Router } from '@angular/router' | 16 | import { Router } from '@angular/router' |
17 | import { Notifier, User } from '@app/core' | 17 | import { Notifier, User } from '@app/core' |
18 | import { VIDEO_COMMENT_TEXT_VALIDATOR } from '@app/shared/form-validators/video-comment-validators' | 18 | import { VIDEO_COMMENT_TEXT_VALIDATOR } from '@app/shared/form-validators/video-comment-validators' |
19 | import { FormReactive, FormValidatorService } from '@app/shared/shared-forms' | 19 | import { FormReactive, FormReactiveService } from '@app/shared/shared-forms' |
20 | import { Video } from '@app/shared/shared-main' | 20 | import { Video } from '@app/shared/shared-main' |
21 | import { VideoComment, VideoCommentService } from '@app/shared/shared-video-comment' | 21 | import { VideoComment, VideoCommentService } from '@app/shared/shared-video-comment' |
22 | import { NgbModal } from '@ng-bootstrap/ng-bootstrap' | 22 | import { NgbModal } from '@ng-bootstrap/ng-bootstrap' |
@@ -48,7 +48,7 @@ export class VideoCommentAddComponent extends FormReactive implements OnChanges, | |||
48 | private emojiMarkupList: { emoji: string, name: string }[] | 48 | private emojiMarkupList: { emoji: string, name: string }[] |
49 | 49 | ||
50 | constructor ( | 50 | constructor ( |
51 | protected formValidatorService: FormValidatorService, | 51 | protected formReactiveService: FormReactiveService, |
52 | private notifier: Notifier, | 52 | private notifier: Notifier, |
53 | private videoCommentService: VideoCommentService, | 53 | private videoCommentService: VideoCommentService, |
54 | private modalService: NgbModal, | 54 | private modalService: NgbModal, |
diff --git a/client/src/app/core/auth/auth.service.ts b/client/src/app/core/auth/auth.service.ts index ca46866f5..7f4fae4aa 100644 --- a/client/src/app/core/auth/auth.service.ts +++ b/client/src/app/core/auth/auth.service.ts | |||
@@ -1,7 +1,7 @@ | |||
1 | import { Hotkey, HotkeysService } from 'angular2-hotkeys' | 1 | import { Hotkey, HotkeysService } from 'angular2-hotkeys' |
2 | import { Observable, ReplaySubject, Subject, throwError as observableThrowError } from 'rxjs' | 2 | import { Observable, ReplaySubject, Subject, throwError as observableThrowError } from 'rxjs' |
3 | import { catchError, map, mergeMap, share, tap } from 'rxjs/operators' | 3 | import { catchError, map, mergeMap, share, tap } from 'rxjs/operators' |
4 | import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http' | 4 | import { HttpClient, HttpErrorResponse, HttpHeaders, HttpParams } from '@angular/common/http' |
5 | import { Injectable } from '@angular/core' | 5 | import { Injectable } from '@angular/core' |
6 | import { Router } from '@angular/router' | 6 | import { Router } from '@angular/router' |
7 | import { Notifier } from '@app/core/notification/notifier.service' | 7 | import { Notifier } from '@app/core/notification/notifier.service' |
@@ -141,7 +141,14 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular | |||
141 | return !!this.getAccessToken() | 141 | return !!this.getAccessToken() |
142 | } | 142 | } |
143 | 143 | ||
144 | login (username: string, password: string, token?: string) { | 144 | login (options: { |
145 | username: string | ||
146 | password: string | ||
147 | otpToken?: string | ||
148 | token?: string | ||
149 | }) { | ||
150 | const { username, password, token, otpToken } = options | ||
151 | |||
145 | // Form url encoded | 152 | // Form url encoded |
146 | const body = { | 153 | const body = { |
147 | client_id: this.clientId, | 154 | client_id: this.clientId, |
@@ -155,7 +162,9 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular | |||
155 | 162 | ||
156 | if (token) Object.assign(body, { externalAuthToken: token }) | 163 | if (token) Object.assign(body, { externalAuthToken: token }) |
157 | 164 | ||
158 | const headers = new HttpHeaders().set('Content-Type', 'application/x-www-form-urlencoded') | 165 | let headers = new HttpHeaders().set('Content-Type', 'application/x-www-form-urlencoded') |
166 | if (otpToken) headers = headers.set('x-peertube-otp', otpToken) | ||
167 | |||
159 | return this.http.post<UserLogin>(AuthService.BASE_TOKEN_URL, objectToUrlEncoded(body), { headers }) | 168 | return this.http.post<UserLogin>(AuthService.BASE_TOKEN_URL, objectToUrlEncoded(body), { headers }) |
160 | .pipe( | 169 | .pipe( |
161 | map(res => Object.assign(res, { username })), | 170 | map(res => Object.assign(res, { username })), |
@@ -245,6 +254,14 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular | |||
245 | }) | 254 | }) |
246 | } | 255 | } |
247 | 256 | ||
257 | isOTPMissingError (err: HttpErrorResponse) { | ||
258 | if (err.status !== HttpStatusCode.UNAUTHORIZED_401) return false | ||
259 | |||
260 | if (err.headers.get('x-peertube-otp') !== 'required; app') return false | ||
261 | |||
262 | return true | ||
263 | } | ||
264 | |||
248 | private mergeUserInformation (obj: UserLoginWithUsername): Observable<UserLoginWithUserInformation> { | 265 | private mergeUserInformation (obj: UserLoginWithUsername): Observable<UserLoginWithUserInformation> { |
249 | // User is not loaded yet, set manually auth header | 266 | // User is not loaded yet, set manually auth header |
250 | const headers = new HttpHeaders().set('Authorization', `${obj.token_type} ${obj.access_token}`) | 267 | const headers = new HttpHeaders().set('Authorization', `${obj.token_type} ${obj.access_token}`) |
diff --git a/client/src/app/core/confirm/confirm.service.ts b/client/src/app/core/confirm/confirm.service.ts index 338b8762c..89a25f0a5 100644 --- a/client/src/app/core/confirm/confirm.service.ts +++ b/client/src/app/core/confirm/confirm.service.ts | |||
@@ -1,28 +1,53 @@ | |||
1 | import { firstValueFrom, Subject } from 'rxjs' | 1 | import { firstValueFrom, map, Observable, Subject } from 'rxjs' |
2 | import { Injectable } from '@angular/core' | 2 | import { Injectable } from '@angular/core' |
3 | 3 | ||
4 | type ConfirmOptions = { | 4 | type ConfirmOptions = { |
5 | title: string | 5 | title: string |
6 | message: string | 6 | message: string |
7 | inputLabel?: string | 7 | } & ( |
8 | expectedInputValue?: string | 8 | { |
9 | confirmButtonText?: string | 9 | type: 'confirm' |
10 | } | 10 | confirmButtonText?: string |
11 | } | | ||
12 | { | ||
13 | type: 'confirm-password' | ||
14 | confirmButtonText?: string | ||
15 | } | | ||
16 | { | ||
17 | type: 'confirm-expected-input' | ||
18 | inputLabel?: string | ||
19 | expectedInputValue?: string | ||
20 | confirmButtonText?: string | ||
21 | } | ||
22 | ) | ||
11 | 23 | ||
12 | @Injectable() | 24 | @Injectable() |
13 | export class ConfirmService { | 25 | export class ConfirmService { |
14 | showConfirm = new Subject<ConfirmOptions>() | 26 | showConfirm = new Subject<ConfirmOptions>() |
15 | confirmResponse = new Subject<boolean>() | 27 | confirmResponse = new Subject<{ confirmed: boolean, value?: string }>() |
16 | 28 | ||
17 | confirm (message: string, title = '', confirmButtonText?: string) { | 29 | confirm (message: string, title = '', confirmButtonText?: string) { |
18 | this.showConfirm.next({ title, message, confirmButtonText }) | 30 | this.showConfirm.next({ type: 'confirm', title, message, confirmButtonText }) |
31 | |||
32 | return firstValueFrom(this.extractConfirmed(this.confirmResponse.asObservable())) | ||
33 | } | ||
19 | 34 | ||
20 | return firstValueFrom(this.confirmResponse.asObservable()) | 35 | confirmWithPassword (message: string, title = '', confirmButtonText?: string) { |
36 | this.showConfirm.next({ type: 'confirm-password', title, message, confirmButtonText }) | ||
37 | |||
38 | const obs = this.confirmResponse.asObservable() | ||
39 | .pipe(map(({ confirmed, value }) => ({ confirmed, password: value }))) | ||
40 | |||
41 | return firstValueFrom(obs) | ||
21 | } | 42 | } |
22 | 43 | ||
23 | confirmWithInput (message: string, inputLabel: string, expectedInputValue: string, title = '', confirmButtonText?: string) { | 44 | confirmWithExpectedInput (message: string, inputLabel: string, expectedInputValue: string, title = '', confirmButtonText?: string) { |
24 | this.showConfirm.next({ title, message, inputLabel, expectedInputValue, confirmButtonText }) | 45 | this.showConfirm.next({ type: 'confirm-expected-input', title, message, inputLabel, expectedInputValue, confirmButtonText }) |
46 | |||
47 | return firstValueFrom(this.extractConfirmed(this.confirmResponse.asObservable())) | ||
48 | } | ||
25 | 49 | ||
26 | return firstValueFrom(this.confirmResponse.asObservable()) | 50 | private extractConfirmed (obs: Observable<{ confirmed: boolean }>) { |
51 | return obs.pipe(map(({ confirmed }) => confirmed)) | ||
27 | } | 52 | } |
28 | } | 53 | } |
diff --git a/client/src/app/core/rest/rest-extractor.service.ts b/client/src/app/core/rest/rest-extractor.service.ts index 7eec2eca6..57dd9ae26 100644 --- a/client/src/app/core/rest/rest-extractor.service.ts +++ b/client/src/app/core/rest/rest-extractor.service.ts | |||
@@ -4,6 +4,7 @@ import { Router } from '@angular/router' | |||
4 | import { DateFormat, dateToHuman } from '@app/helpers' | 4 | import { DateFormat, dateToHuman } from '@app/helpers' |
5 | import { logger } from '@root-helpers/logger' | 5 | import { logger } from '@root-helpers/logger' |
6 | import { HttpStatusCode, ResultList } from '@shared/models' | 6 | import { HttpStatusCode, ResultList } from '@shared/models' |
7 | import { HttpHeaderResponse } from '@angular/common/http' | ||
7 | 8 | ||
8 | @Injectable() | 9 | @Injectable() |
9 | export class RestExtractor { | 10 | export class RestExtractor { |
@@ -54,10 +55,11 @@ export class RestExtractor { | |||
54 | handleError (err: any) { | 55 | handleError (err: any) { |
55 | const errorMessage = this.buildErrorMessage(err) | 56 | const errorMessage = this.buildErrorMessage(err) |
56 | 57 | ||
57 | const errorObj: { message: string, status: string, body: string } = { | 58 | const errorObj: { message: string, status: string, body: string, headers: HttpHeaderResponse } = { |
58 | message: errorMessage, | 59 | message: errorMessage, |
59 | status: undefined, | 60 | status: undefined, |
60 | body: undefined | 61 | body: undefined, |
62 | headers: err.headers | ||
61 | } | 63 | } |
62 | 64 | ||
63 | if (err.status) { | 65 | if (err.status) { |
diff --git a/client/src/app/core/users/user.model.ts b/client/src/app/core/users/user.model.ts index 6ba30e4b8..8385a4012 100644 --- a/client/src/app/core/users/user.model.ts +++ b/client/src/app/core/users/user.model.ts | |||
@@ -66,6 +66,8 @@ export class User implements UserServerModel { | |||
66 | 66 | ||
67 | lastLoginDate: Date | null | 67 | lastLoginDate: Date | null |
68 | 68 | ||
69 | twoFactorEnabled: boolean | ||
70 | |||
69 | createdAt: Date | 71 | createdAt: Date |
70 | 72 | ||
71 | constructor (hash: Partial<UserServerModel>) { | 73 | constructor (hash: Partial<UserServerModel>) { |
@@ -108,6 +110,8 @@ export class User implements UserServerModel { | |||
108 | 110 | ||
109 | this.notificationSettings = hash.notificationSettings | 111 | this.notificationSettings = hash.notificationSettings |
110 | 112 | ||
113 | this.twoFactorEnabled = hash.twoFactorEnabled | ||
114 | |||
111 | this.createdAt = hash.createdAt | 115 | this.createdAt = hash.createdAt |
112 | 116 | ||
113 | this.pluginAuth = hash.pluginAuth | 117 | this.pluginAuth = hash.pluginAuth |
diff --git a/client/src/app/modal/confirm.component.html b/client/src/app/modal/confirm.component.html index c59c25770..f364165c4 100644 --- a/client/src/app/modal/confirm.component.html +++ b/client/src/app/modal/confirm.component.html | |||
@@ -9,9 +9,12 @@ | |||
9 | <div class="modal-body" > | 9 | <div class="modal-body" > |
10 | <div [innerHtml]="message"></div> | 10 | <div [innerHtml]="message"></div> |
11 | 11 | ||
12 | <div *ngIf="inputLabel && expectedInputValue" class="form-group mt-3"> | 12 | <div *ngIf="inputLabel" class="form-group mt-3"> |
13 | <label for="confirmInput">{{ inputLabel }}</label> | 13 | <label for="confirmInput">{{ inputLabel }}</label> |
14 | <input type="text" id="confirmInput" name="confirmInput" [(ngModel)]="inputValue" /> | 14 | |
15 | <input *ngIf="!isPasswordInput" type="text" id="confirmInput" name="confirmInput" [(ngModel)]="inputValue" /> | ||
16 | |||
17 | <my-input-text inputId="confirmInput" [(ngModel)]="inputValue"></my-input-text> | ||
15 | </div> | 18 | </div> |
16 | </div> | 19 | </div> |
17 | 20 | ||
diff --git a/client/src/app/modal/confirm.component.ts b/client/src/app/modal/confirm.component.ts index ec4e1d60f..3bb8b9b21 100644 --- a/client/src/app/modal/confirm.component.ts +++ b/client/src/app/modal/confirm.component.ts | |||
@@ -21,6 +21,8 @@ export class ConfirmComponent implements OnInit { | |||
21 | inputValue = '' | 21 | inputValue = '' |
22 | confirmButtonText = '' | 22 | confirmButtonText = '' |
23 | 23 | ||
24 | isPasswordInput = false | ||
25 | |||
24 | private openedModal: NgbModalRef | 26 | private openedModal: NgbModalRef |
25 | 27 | ||
26 | constructor ( | 28 | constructor ( |
@@ -31,11 +33,27 @@ export class ConfirmComponent implements OnInit { | |||
31 | 33 | ||
32 | ngOnInit () { | 34 | ngOnInit () { |
33 | this.confirmService.showConfirm.subscribe( | 35 | this.confirmService.showConfirm.subscribe( |
34 | ({ title, message, expectedInputValue, inputLabel, confirmButtonText }) => { | 36 | payload => { |
37 | // Reinit fields | ||
38 | this.title = '' | ||
39 | this.message = '' | ||
40 | this.expectedInputValue = '' | ||
41 | this.inputLabel = '' | ||
42 | this.inputValue = '' | ||
43 | this.confirmButtonText = '' | ||
44 | this.isPasswordInput = false | ||
45 | |||
46 | const { type, title, message, confirmButtonText } = payload | ||
47 | |||
35 | this.title = title | 48 | this.title = title |
36 | 49 | ||
37 | this.inputLabel = inputLabel | 50 | if (type === 'confirm-expected-input') { |
38 | this.expectedInputValue = expectedInputValue | 51 | this.inputLabel = payload.inputLabel |
52 | this.expectedInputValue = payload.expectedInputValue | ||
53 | } else if (type === 'confirm-password') { | ||
54 | this.inputLabel = $localize`Confirm your password` | ||
55 | this.isPasswordInput = true | ||
56 | } | ||
39 | 57 | ||
40 | this.confirmButtonText = confirmButtonText || $localize`Confirm` | 58 | this.confirmButtonText = confirmButtonText || $localize`Confirm` |
41 | 59 | ||
@@ -66,11 +84,13 @@ export class ConfirmComponent implements OnInit { | |||
66 | this.openedModal = this.modalService.open(this.confirmModal, { centered: true }) | 84 | this.openedModal = this.modalService.open(this.confirmModal, { centered: true }) |
67 | 85 | ||
68 | this.openedModal.result | 86 | this.openedModal.result |
69 | .then(() => this.confirmService.confirmResponse.next(true)) | 87 | .then(() => { |
88 | this.confirmService.confirmResponse.next({ confirmed: true, value: this.inputValue }) | ||
89 | }) | ||
70 | .catch((reason: string) => { | 90 | .catch((reason: string) => { |
71 | // If the reason was that the user used the back button, we don't care about the confirm dialog result | 91 | // If the reason was that the user used the back button, we don't care about the confirm dialog result |
72 | if (!reason || reason !== POP_STATE_MODAL_DISMISS) { | 92 | if (!reason || reason !== POP_STATE_MODAL_DISMISS) { |
73 | this.confirmService.confirmResponse.next(false) | 93 | this.confirmService.confirmResponse.next({ confirmed: false, value: this.inputValue }) |
74 | } | 94 | } |
75 | }) | 95 | }) |
76 | } | 96 | } |
diff --git a/client/src/app/shared/form-validators/user-validators.ts b/client/src/app/shared/form-validators/user-validators.ts index 3262853d8..b93de75ea 100644 --- a/client/src/app/shared/form-validators/user-validators.ts +++ b/client/src/app/shared/form-validators/user-validators.ts | |||
@@ -61,6 +61,15 @@ export const USER_EXISTING_PASSWORD_VALIDATOR: BuildFormValidator = { | |||
61 | } | 61 | } |
62 | } | 62 | } |
63 | 63 | ||
64 | export const USER_OTP_TOKEN_VALIDATOR: BuildFormValidator = { | ||
65 | VALIDATORS: [ | ||
66 | Validators.required | ||
67 | ], | ||
68 | MESSAGES: { | ||
69 | required: $localize`OTP token is required.` | ||
70 | } | ||
71 | } | ||
72 | |||
64 | export const USER_PASSWORD_VALIDATOR = { | 73 | export const USER_PASSWORD_VALIDATOR = { |
65 | VALIDATORS: [ | 74 | VALIDATORS: [ |
66 | Validators.required, | 75 | Validators.required, |
diff --git a/client/src/app/shared/shared-abuse-list/abuse-message-modal.component.ts b/client/src/app/shared/shared-abuse-list/abuse-message-modal.component.ts index d24a5d58d..12d503f56 100644 --- a/client/src/app/shared/shared-abuse-list/abuse-message-modal.component.ts +++ b/client/src/app/shared/shared-abuse-list/abuse-message-modal.component.ts | |||
@@ -1,6 +1,6 @@ | |||
1 | import { Component, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core' | 1 | import { Component, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core' |
2 | import { AuthService, HtmlRendererService, Notifier } from '@app/core' | 2 | import { AuthService, HtmlRendererService, Notifier } from '@app/core' |
3 | import { FormReactive, FormValidatorService } from '@app/shared/shared-forms' | 3 | import { FormReactive, FormReactiveService } 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 { logger } from '@root-helpers/logger' | 6 | import { logger } from '@root-helpers/logger' |
@@ -29,7 +29,7 @@ export class AbuseMessageModalComponent extends FormReactive implements OnInit { | |||
29 | private abuse: UserAbuse | 29 | private abuse: UserAbuse |
30 | 30 | ||
31 | constructor ( | 31 | constructor ( |
32 | protected formValidatorService: FormValidatorService, | 32 | protected formReactiveService: FormReactiveService, |
33 | private modalService: NgbModal, | 33 | private modalService: NgbModal, |
34 | private htmlRenderer: HtmlRendererService, | 34 | private htmlRenderer: HtmlRendererService, |
35 | private auth: AuthService, | 35 | private auth: AuthService, |
diff --git a/client/src/app/shared/shared-abuse-list/moderation-comment-modal.component.ts b/client/src/app/shared/shared-abuse-list/moderation-comment-modal.component.ts index 2600da8da..4ad807d25 100644 --- a/client/src/app/shared/shared-abuse-list/moderation-comment-modal.component.ts +++ b/client/src/app/shared/shared-abuse-list/moderation-comment-modal.component.ts | |||
@@ -1,6 +1,6 @@ | |||
1 | import { Component, EventEmitter, OnInit, Output, ViewChild } from '@angular/core' | 1 | import { Component, EventEmitter, OnInit, Output, ViewChild } from '@angular/core' |
2 | import { Notifier } from '@app/core' | 2 | import { Notifier } from '@app/core' |
3 | import { FormReactive, FormValidatorService } from '@app/shared/shared-forms' | 3 | import { FormReactive, FormReactiveService } from '@app/shared/shared-forms' |
4 | import { AbuseService } from '@app/shared/shared-moderation' | 4 | import { AbuseService } from '@app/shared/shared-moderation' |
5 | import { NgbModal } from '@ng-bootstrap/ng-bootstrap' | 5 | import { NgbModal } from '@ng-bootstrap/ng-bootstrap' |
6 | import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap/modal/modal-ref' | 6 | import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap/modal/modal-ref' |
@@ -20,7 +20,7 @@ export class ModerationCommentModalComponent extends FormReactive implements OnI | |||
20 | private openedModal: NgbModalRef | 20 | private openedModal: NgbModalRef |
21 | 21 | ||
22 | constructor ( | 22 | constructor ( |
23 | protected formValidatorService: FormValidatorService, | 23 | protected formReactiveService: FormReactiveService, |
24 | private modalService: NgbModal, | 24 | private modalService: NgbModal, |
25 | private notifier: Notifier, | 25 | private notifier: Notifier, |
26 | private abuseService: AbuseService | 26 | private abuseService: AbuseService |
diff --git a/client/src/app/shared/shared-forms/form-reactive.service.ts b/client/src/app/shared/shared-forms/form-reactive.service.ts new file mode 100644 index 000000000..f1b7e0ef2 --- /dev/null +++ b/client/src/app/shared/shared-forms/form-reactive.service.ts | |||
@@ -0,0 +1,101 @@ | |||
1 | import { Injectable } from '@angular/core' | ||
2 | import { AbstractControl, FormGroup } from '@angular/forms' | ||
3 | import { wait } from '@root-helpers/utils' | ||
4 | import { BuildFormArgument, BuildFormDefaultValues } from '../form-validators/form-validator.model' | ||
5 | import { FormValidatorService } from './form-validator.service' | ||
6 | |||
7 | export type FormReactiveErrors = { [ id: string ]: string | FormReactiveErrors } | ||
8 | export type FormReactiveValidationMessages = { | ||
9 | [ id: string ]: { [ name: string ]: string } | FormReactiveValidationMessages | ||
10 | } | ||
11 | |||
12 | @Injectable() | ||
13 | export class FormReactiveService { | ||
14 | |||
15 | constructor (private formValidatorService: FormValidatorService) { | ||
16 | |||
17 | } | ||
18 | |||
19 | buildForm (obj: BuildFormArgument, defaultValues: BuildFormDefaultValues = {}) { | ||
20 | const { formErrors, validationMessages, form } = this.formValidatorService.buildForm(obj, defaultValues) | ||
21 | |||
22 | form.statusChanges.subscribe(async () => { | ||
23 | // FIXME: remove when https://github.com/angular/angular/issues/41519 is fixed | ||
24 | await this.waitPendingCheck(form) | ||
25 | |||
26 | this.onStatusChanged({ form, formErrors, validationMessages }) | ||
27 | }) | ||
28 | |||
29 | return { form, formErrors, validationMessages } | ||
30 | } | ||
31 | |||
32 | async waitPendingCheck (form: FormGroup) { | ||
33 | if (form.status !== 'PENDING') return | ||
34 | |||
35 | // FIXME: the following line does not work: https://github.com/angular/angular/issues/41519 | ||
36 | // return firstValueFrom(form.statusChanges.pipe(filter(status => status !== 'PENDING'))) | ||
37 | // So we have to fallback to active wait :/ | ||
38 | |||
39 | do { | ||
40 | await wait(10) | ||
41 | } while (form.status === 'PENDING') | ||
42 | } | ||
43 | |||
44 | markAllAsDirty (controlsArg: { [ key: string ]: AbstractControl }) { | ||
45 | const controls = controlsArg | ||
46 | |||
47 | for (const key of Object.keys(controls)) { | ||
48 | const control = controls[key] | ||
49 | |||
50 | if (control instanceof FormGroup) { | ||
51 | this.markAllAsDirty(control.controls) | ||
52 | continue | ||
53 | } | ||
54 | |||
55 | control.markAsDirty() | ||
56 | } | ||
57 | } | ||
58 | |||
59 | forceCheck (form: FormGroup, formErrors: any, validationMessages: FormReactiveValidationMessages) { | ||
60 | this.onStatusChanged({ form, formErrors, validationMessages, onlyDirty: false }) | ||
61 | } | ||
62 | |||
63 | private onStatusChanged (options: { | ||
64 | form: FormGroup | ||
65 | formErrors: FormReactiveErrors | ||
66 | validationMessages: FormReactiveValidationMessages | ||
67 | onlyDirty?: boolean // default true | ||
68 | }) { | ||
69 | const { form, formErrors, validationMessages, onlyDirty = true } = options | ||
70 | |||
71 | for (const field of Object.keys(formErrors)) { | ||
72 | if (formErrors[field] && typeof formErrors[field] === 'object') { | ||
73 | this.onStatusChanged({ | ||
74 | form: form.controls[field] as FormGroup, | ||
75 | formErrors: formErrors[field] as FormReactiveErrors, | ||
76 | validationMessages: validationMessages[field] as FormReactiveValidationMessages, | ||
77 | onlyDirty | ||
78 | }) | ||
79 | |||
80 | continue | ||
81 | } | ||
82 | |||
83 | // clear previous error message (if any) | ||
84 | formErrors[field] = '' | ||
85 | const control = form.get(field) | ||
86 | |||
87 | if (!control || (onlyDirty && !control.dirty) || !control.enabled || !control.errors) continue | ||
88 | |||
89 | const staticMessages = validationMessages[field] | ||
90 | for (const key of Object.keys(control.errors)) { | ||
91 | const formErrorValue = control.errors[key] | ||
92 | |||
93 | // Try to find error message in static validation messages first | ||
94 | // Then check if the validator returns a string that is the error | ||
95 | if (staticMessages[key]) formErrors[field] += staticMessages[key] + ' ' | ||
96 | else if (typeof formErrorValue === 'string') formErrors[field] += control.errors[key] | ||
97 | else throw new Error('Form error value of ' + field + ' is invalid') | ||
98 | } | ||
99 | } | ||
100 | } | ||
101 | } | ||
diff --git a/client/src/app/shared/shared-forms/form-reactive.ts b/client/src/app/shared/shared-forms/form-reactive.ts index a19ffdd82..d1e7be802 100644 --- a/client/src/app/shared/shared-forms/form-reactive.ts +++ b/client/src/app/shared/shared-forms/form-reactive.ts | |||
@@ -1,16 +1,9 @@ | |||
1 | 1 | import { FormGroup } from '@angular/forms' | |
2 | import { AbstractControl, FormGroup } from '@angular/forms' | ||
3 | import { wait } from '@root-helpers/utils' | ||
4 | import { BuildFormArgument, BuildFormDefaultValues } from '../form-validators/form-validator.model' | 2 | import { BuildFormArgument, BuildFormDefaultValues } from '../form-validators/form-validator.model' |
5 | import { FormValidatorService } from './form-validator.service' | 3 | import { FormReactiveService, FormReactiveValidationMessages } from './form-reactive.service' |
6 | |||
7 | export type FormReactiveErrors = { [ id: string ]: string | FormReactiveErrors } | ||
8 | export type FormReactiveValidationMessages = { | ||
9 | [ id: string ]: { [ name: string ]: string } | FormReactiveValidationMessages | ||
10 | } | ||
11 | 4 | ||
12 | export abstract class FormReactive { | 5 | export abstract class FormReactive { |
13 | protected abstract formValidatorService: FormValidatorService | 6 | protected abstract formReactiveService: FormReactiveService |
14 | protected formChanged = false | 7 | protected formChanged = false |
15 | 8 | ||
16 | form: FormGroup | 9 | form: FormGroup |
@@ -18,86 +11,22 @@ export abstract class FormReactive { | |||
18 | validationMessages: FormReactiveValidationMessages | 11 | validationMessages: FormReactiveValidationMessages |
19 | 12 | ||
20 | buildForm (obj: BuildFormArgument, defaultValues: BuildFormDefaultValues = {}) { | 13 | buildForm (obj: BuildFormArgument, defaultValues: BuildFormDefaultValues = {}) { |
21 | const { formErrors, validationMessages, form } = this.formValidatorService.buildForm(obj, defaultValues) | 14 | const { formErrors, validationMessages, form } = this.formReactiveService.buildForm(obj, defaultValues) |
22 | 15 | ||
23 | this.form = form | 16 | this.form = form |
24 | this.formErrors = formErrors | 17 | this.formErrors = formErrors |
25 | this.validationMessages = validationMessages | 18 | this.validationMessages = validationMessages |
26 | |||
27 | this.form.statusChanges.subscribe(async () => { | ||
28 | // FIXME: remove when https://github.com/angular/angular/issues/41519 is fixed | ||
29 | await this.waitPendingCheck() | ||
30 | |||
31 | this.onStatusChanged(this.form, this.formErrors, this.validationMessages) | ||
32 | }) | ||
33 | } | 19 | } |
34 | 20 | ||
35 | protected async waitPendingCheck () { | 21 | protected async waitPendingCheck () { |
36 | if (this.form.status !== 'PENDING') return | 22 | return this.formReactiveService.waitPendingCheck(this.form) |
37 | |||
38 | // FIXME: the following line does not work: https://github.com/angular/angular/issues/41519 | ||
39 | // return firstValueFrom(this.form.statusChanges.pipe(filter(status => status !== 'PENDING'))) | ||
40 | // So we have to fallback to active wait :/ | ||
41 | |||
42 | do { | ||
43 | await wait(10) | ||
44 | } while (this.form.status === 'PENDING') | ||
45 | } | 23 | } |
46 | 24 | ||
47 | protected markAllAsDirty (controlsArg?: { [ key: string ]: AbstractControl }) { | 25 | protected markAllAsDirty () { |
48 | const controls = controlsArg || this.form.controls | 26 | return this.formReactiveService.markAllAsDirty(this.form.controls) |
49 | |||
50 | for (const key of Object.keys(controls)) { | ||
51 | const control = controls[key] | ||
52 | |||
53 | if (control instanceof FormGroup) { | ||
54 | this.markAllAsDirty(control.controls) | ||
55 | continue | ||
56 | } | ||
57 | |||
58 | control.markAsDirty() | ||
59 | } | ||
60 | } | 27 | } |
61 | 28 | ||
62 | protected forceCheck () { | 29 | protected forceCheck () { |
63 | this.onStatusChanged(this.form, this.formErrors, this.validationMessages, false) | 30 | return this.formReactiveService.forceCheck(this.form, this.formErrors, this.validationMessages) |
64 | } | ||
65 | |||
66 | private onStatusChanged ( | ||
67 | form: FormGroup, | ||
68 | formErrors: FormReactiveErrors, | ||
69 | validationMessages: FormReactiveValidationMessages, | ||
70 | onlyDirty = true | ||
71 | ) { | ||
72 | for (const field of Object.keys(formErrors)) { | ||
73 | if (formErrors[field] && typeof formErrors[field] === 'object') { | ||
74 | this.onStatusChanged( | ||
75 | form.controls[field] as FormGroup, | ||
76 | formErrors[field] as FormReactiveErrors, | ||
77 | validationMessages[field] as FormReactiveValidationMessages, | ||
78 | onlyDirty | ||
79 | ) | ||
80 | continue | ||
81 | } | ||
82 | |||
83 | // clear previous error message (if any) | ||
84 | formErrors[field] = '' | ||
85 | const control = form.get(field) | ||
86 | |||
87 | if (control.dirty) this.formChanged = true | ||
88 | |||
89 | if (!control || (onlyDirty && !control.dirty) || !control.enabled || !control.errors) continue | ||
90 | |||
91 | const staticMessages = validationMessages[field] | ||
92 | for (const key of Object.keys(control.errors)) { | ||
93 | const formErrorValue = control.errors[key] | ||
94 | |||
95 | // Try to find error message in static validation messages first | ||
96 | // Then check if the validator returns a string that is the error | ||
97 | if (staticMessages[key]) formErrors[field] += staticMessages[key] + ' ' | ||
98 | else if (typeof formErrorValue === 'string') formErrors[field] += control.errors[key] | ||
99 | else throw new Error('Form error value of ' + field + ' is invalid') | ||
100 | } | ||
101 | } | ||
102 | } | 31 | } |
103 | } | 32 | } |
diff --git a/client/src/app/shared/shared-forms/form-validator.service.ts b/client/src/app/shared/shared-forms/form-validator.service.ts index f67d5bb33..897008242 100644 --- a/client/src/app/shared/shared-forms/form-validator.service.ts +++ b/client/src/app/shared/shared-forms/form-validator.service.ts | |||
@@ -1,7 +1,7 @@ | |||
1 | import { Injectable } from '@angular/core' | 1 | import { Injectable } from '@angular/core' |
2 | import { AsyncValidatorFn, FormArray, FormBuilder, FormControl, FormGroup, ValidatorFn } from '@angular/forms' | 2 | import { AsyncValidatorFn, FormArray, FormBuilder, FormControl, FormGroup, ValidatorFn } from '@angular/forms' |
3 | import { BuildFormArgument, BuildFormDefaultValues } from '../form-validators/form-validator.model' | 3 | import { BuildFormArgument, BuildFormDefaultValues } from '../form-validators/form-validator.model' |
4 | import { FormReactiveErrors, FormReactiveValidationMessages } from './form-reactive' | 4 | import { FormReactiveErrors, FormReactiveValidationMessages } from './form-reactive.service' |
5 | 5 | ||
6 | @Injectable() | 6 | @Injectable() |
7 | export class FormValidatorService { | 7 | export class FormValidatorService { |
diff --git a/client/src/app/shared/shared-forms/index.ts b/client/src/app/shared/shared-forms/index.ts index 495785e7b..bff9862f2 100644 --- a/client/src/app/shared/shared-forms/index.ts +++ b/client/src/app/shared/shared-forms/index.ts | |||
@@ -1,4 +1,5 @@ | |||
1 | export * from './advanced-input-filter.component' | 1 | export * from './advanced-input-filter.component' |
2 | export * from './form-reactive.service' | ||
2 | export * from './form-reactive' | 3 | export * from './form-reactive' |
3 | export * from './form-validator.service' | 4 | export * from './form-validator.service' |
4 | export * from './form-validator.service' | 5 | export * from './form-validator.service' |
diff --git a/client/src/app/shared/shared-forms/input-text.component.ts b/client/src/app/shared/shared-forms/input-text.component.ts index d667ed663..aa4a1cba8 100644 --- a/client/src/app/shared/shared-forms/input-text.component.ts +++ b/client/src/app/shared/shared-forms/input-text.component.ts | |||
@@ -1,4 +1,4 @@ | |||
1 | import { Component, forwardRef, Input } from '@angular/core' | 1 | import { Component, ElementRef, forwardRef, Input, ViewChild } from '@angular/core' |
2 | import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms' | 2 | import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms' |
3 | import { Notifier } from '@app/core' | 3 | import { Notifier } from '@app/core' |
4 | 4 | ||
@@ -15,6 +15,8 @@ import { Notifier } from '@app/core' | |||
15 | ] | 15 | ] |
16 | }) | 16 | }) |
17 | export class InputTextComponent implements ControlValueAccessor { | 17 | export class InputTextComponent implements ControlValueAccessor { |
18 | @ViewChild('input') inputElement: ElementRef | ||
19 | |||
18 | @Input() inputId = Math.random().toString(11).slice(2, 8) // id cannot be left empty or undefined | 20 | @Input() inputId = Math.random().toString(11).slice(2, 8) // id cannot be left empty or undefined |
19 | @Input() value = '' | 21 | @Input() value = '' |
20 | @Input() autocomplete = 'off' | 22 | @Input() autocomplete = 'off' |
@@ -65,4 +67,10 @@ export class InputTextComponent implements ControlValueAccessor { | |||
65 | update () { | 67 | update () { |
66 | this.propagateChange(this.value) | 68 | this.propagateChange(this.value) |
67 | } | 69 | } |
70 | |||
71 | focus () { | ||
72 | const el: HTMLElement = this.inputElement.nativeElement | ||
73 | |||
74 | el.focus({ preventScroll: true }) | ||
75 | } | ||
68 | } | 76 | } |
diff --git a/client/src/app/shared/shared-forms/shared-form.module.ts b/client/src/app/shared/shared-forms/shared-form.module.ts index 81f076db6..628affb56 100644 --- a/client/src/app/shared/shared-forms/shared-form.module.ts +++ b/client/src/app/shared/shared-forms/shared-form.module.ts | |||
@@ -1,4 +1,3 @@ | |||
1 | |||
2 | import { InputMaskModule } from 'primeng/inputmask' | 1 | import { InputMaskModule } from 'primeng/inputmask' |
3 | import { NgModule } from '@angular/core' | 2 | import { NgModule } from '@angular/core' |
4 | import { FormsModule, ReactiveFormsModule } from '@angular/forms' | 3 | import { FormsModule, ReactiveFormsModule } from '@angular/forms' |
@@ -7,6 +6,7 @@ import { SharedGlobalIconModule } from '../shared-icons' | |||
7 | import { SharedMainModule } from '../shared-main/shared-main.module' | 6 | import { SharedMainModule } from '../shared-main/shared-main.module' |
8 | import { AdvancedInputFilterComponent } from './advanced-input-filter.component' | 7 | import { AdvancedInputFilterComponent } from './advanced-input-filter.component' |
9 | import { DynamicFormFieldComponent } from './dynamic-form-field.component' | 8 | import { DynamicFormFieldComponent } from './dynamic-form-field.component' |
9 | import { FormReactiveService } from './form-reactive.service' | ||
10 | import { FormValidatorService } from './form-validator.service' | 10 | import { FormValidatorService } from './form-validator.service' |
11 | import { InputSwitchComponent } from './input-switch.component' | 11 | import { InputSwitchComponent } from './input-switch.component' |
12 | import { InputTextComponent } from './input-text.component' | 12 | import { InputTextComponent } from './input-text.component' |
@@ -96,7 +96,8 @@ import { TimestampInputComponent } from './timestamp-input.component' | |||
96 | ], | 96 | ], |
97 | 97 | ||
98 | providers: [ | 98 | providers: [ |
99 | FormValidatorService | 99 | FormValidatorService, |
100 | FormReactiveService | ||
100 | ] | 101 | ] |
101 | }) | 102 | }) |
102 | export class SharedFormModule { } | 103 | export class SharedFormModule { } |
diff --git a/client/src/app/shared/shared-main/auth/auth-interceptor.service.ts b/client/src/app/shared/shared-main/auth/auth-interceptor.service.ts index e4b74f3ad..93b3a93d6 100644 --- a/client/src/app/shared/shared-main/auth/auth-interceptor.service.ts +++ b/client/src/app/shared/shared-main/auth/auth-interceptor.service.ts | |||
@@ -27,13 +27,16 @@ export class AuthInterceptor implements HttpInterceptor { | |||
27 | .pipe( | 27 | .pipe( |
28 | catchError((err: HttpErrorResponse) => { | 28 | catchError((err: HttpErrorResponse) => { |
29 | const error = err.error as PeerTubeProblemDocument | 29 | const error = err.error as PeerTubeProblemDocument |
30 | const isOTPMissingError = this.authService.isOTPMissingError(err) | ||
30 | 31 | ||
31 | if (err.status === HttpStatusCode.UNAUTHORIZED_401 && error && error.code === OAuth2ErrorCode.INVALID_TOKEN) { | 32 | if (!isOTPMissingError) { |
32 | return this.handleTokenExpired(req, next) | 33 | if (err.status === HttpStatusCode.UNAUTHORIZED_401 && error && error.code === OAuth2ErrorCode.INVALID_TOKEN) { |
33 | } | 34 | return this.handleTokenExpired(req, next) |
35 | } | ||
34 | 36 | ||
35 | if (err.status === HttpStatusCode.UNAUTHORIZED_401) { | 37 | if (err.status === HttpStatusCode.UNAUTHORIZED_401) { |
36 | return this.handleNotAuthenticated(err) | 38 | return this.handleNotAuthenticated(err) |
39 | } | ||
37 | } | 40 | } |
38 | 41 | ||
39 | return observableThrowError(() => err) | 42 | return observableThrowError(() => err) |
diff --git a/client/src/app/shared/shared-moderation/batch-domains-modal.component.ts b/client/src/app/shared/shared-moderation/batch-domains-modal.component.ts index 20be728f6..ec2fea528 100644 --- a/client/src/app/shared/shared-moderation/batch-domains-modal.component.ts +++ b/client/src/app/shared/shared-moderation/batch-domains-modal.component.ts | |||
@@ -1,5 +1,5 @@ | |||
1 | import { Component, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core' | 1 | import { Component, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core' |
2 | import { FormReactive, FormValidatorService } from '@app/shared/shared-forms' | 2 | import { FormReactive, FormReactiveService } from '@app/shared/shared-forms' |
3 | import { NgbModal } from '@ng-bootstrap/ng-bootstrap' | 3 | import { NgbModal } from '@ng-bootstrap/ng-bootstrap' |
4 | import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap/modal/modal-ref' | 4 | import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap/modal/modal-ref' |
5 | import { splitAndGetNotEmpty, UNIQUE_HOSTS_VALIDATOR } from '../form-validators/host-validators' | 5 | import { splitAndGetNotEmpty, UNIQUE_HOSTS_VALIDATOR } from '../form-validators/host-validators' |
@@ -18,7 +18,7 @@ export class BatchDomainsModalComponent extends FormReactive implements OnInit { | |||
18 | private openedModal: NgbModalRef | 18 | private openedModal: NgbModalRef |
19 | 19 | ||
20 | constructor ( | 20 | constructor ( |
21 | protected formValidatorService: FormValidatorService, | 21 | protected formReactiveService: FormReactiveService, |
22 | private modalService: NgbModal | 22 | private modalService: NgbModal |
23 | ) { | 23 | ) { |
24 | super() | 24 | super() |
diff --git a/client/src/app/shared/shared-moderation/report-modals/account-report.component.ts b/client/src/app/shared/shared-moderation/report-modals/account-report.component.ts index 78c9b3382..d587a9709 100644 --- a/client/src/app/shared/shared-moderation/report-modals/account-report.component.ts +++ b/client/src/app/shared/shared-moderation/report-modals/account-report.component.ts | |||
@@ -2,7 +2,7 @@ import { mapValues, pickBy } from 'lodash-es' | |||
2 | import { Component, OnInit, ViewChild } from '@angular/core' | 2 | import { Component, OnInit, ViewChild } from '@angular/core' |
3 | import { Notifier } from '@app/core' | 3 | import { Notifier } from '@app/core' |
4 | import { ABUSE_REASON_VALIDATOR } from '@app/shared/form-validators/abuse-validators' | 4 | import { ABUSE_REASON_VALIDATOR } from '@app/shared/form-validators/abuse-validators' |
5 | import { FormReactive, FormValidatorService } from '@app/shared/shared-forms' | 5 | import { FormReactive, FormReactiveService } from '@app/shared/shared-forms' |
6 | import { Account } from '@app/shared/shared-main' | 6 | import { Account } from '@app/shared/shared-main' |
7 | import { NgbModal } from '@ng-bootstrap/ng-bootstrap' | 7 | import { NgbModal } from '@ng-bootstrap/ng-bootstrap' |
8 | import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap/modal/modal-ref' | 8 | import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap/modal/modal-ref' |
@@ -26,7 +26,7 @@ export class AccountReportComponent extends FormReactive implements OnInit { | |||
26 | private openedModal: NgbModalRef | 26 | private openedModal: NgbModalRef |
27 | 27 | ||
28 | constructor ( | 28 | constructor ( |
29 | protected formValidatorService: FormValidatorService, | 29 | protected formReactiveService: FormReactiveService, |
30 | private modalService: NgbModal, | 30 | private modalService: NgbModal, |
31 | private abuseService: AbuseService, | 31 | private abuseService: AbuseService, |
32 | private notifier: Notifier | 32 | private notifier: Notifier |
diff --git a/client/src/app/shared/shared-moderation/report-modals/comment-report.component.ts b/client/src/app/shared/shared-moderation/report-modals/comment-report.component.ts index 7c0907ce4..e35d70c8f 100644 --- a/client/src/app/shared/shared-moderation/report-modals/comment-report.component.ts +++ b/client/src/app/shared/shared-moderation/report-modals/comment-report.component.ts | |||
@@ -2,7 +2,7 @@ import { mapValues, pickBy } from 'lodash-es' | |||
2 | import { Component, Input, OnInit, ViewChild } from '@angular/core' | 2 | import { Component, Input, OnInit, ViewChild } from '@angular/core' |
3 | import { Notifier } from '@app/core' | 3 | import { Notifier } from '@app/core' |
4 | import { ABUSE_REASON_VALIDATOR } from '@app/shared/form-validators/abuse-validators' | 4 | import { ABUSE_REASON_VALIDATOR } from '@app/shared/form-validators/abuse-validators' |
5 | import { FormReactive, FormValidatorService } from '@app/shared/shared-forms' | 5 | import { FormReactive, FormReactiveService } from '@app/shared/shared-forms' |
6 | import { VideoComment } from '@app/shared/shared-video-comment' | 6 | import { VideoComment } from '@app/shared/shared-video-comment' |
7 | import { NgbModal } from '@ng-bootstrap/ng-bootstrap' | 7 | import { NgbModal } from '@ng-bootstrap/ng-bootstrap' |
8 | import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap/modal/modal-ref' | 8 | import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap/modal/modal-ref' |
@@ -27,7 +27,7 @@ export class CommentReportComponent extends FormReactive implements OnInit { | |||
27 | private openedModal: NgbModalRef | 27 | private openedModal: NgbModalRef |
28 | 28 | ||
29 | constructor ( | 29 | constructor ( |
30 | protected formValidatorService: FormValidatorService, | 30 | protected formReactiveService: FormReactiveService, |
31 | private modalService: NgbModal, | 31 | private modalService: NgbModal, |
32 | private abuseService: AbuseService, | 32 | private abuseService: AbuseService, |
33 | private notifier: Notifier | 33 | private notifier: Notifier |
diff --git a/client/src/app/shared/shared-moderation/report-modals/video-report.component.ts b/client/src/app/shared/shared-moderation/report-modals/video-report.component.ts index 38dd92910..16be8e0a1 100644 --- a/client/src/app/shared/shared-moderation/report-modals/video-report.component.ts +++ b/client/src/app/shared/shared-moderation/report-modals/video-report.component.ts | |||
@@ -3,7 +3,7 @@ import { Component, Input, OnInit, ViewChild } from '@angular/core' | |||
3 | import { DomSanitizer } from '@angular/platform-browser' | 3 | import { DomSanitizer } from '@angular/platform-browser' |
4 | import { Notifier } from '@app/core' | 4 | import { Notifier } from '@app/core' |
5 | import { ABUSE_REASON_VALIDATOR } from '@app/shared/form-validators/abuse-validators' | 5 | import { ABUSE_REASON_VALIDATOR } from '@app/shared/form-validators/abuse-validators' |
6 | import { FormReactive, FormValidatorService } from '@app/shared/shared-forms' | 6 | import { FormReactive, FormReactiveService } from '@app/shared/shared-forms' |
7 | import { NgbModal } from '@ng-bootstrap/ng-bootstrap' | 7 | import { NgbModal } from '@ng-bootstrap/ng-bootstrap' |
8 | import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap/modal/modal-ref' | 8 | import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap/modal/modal-ref' |
9 | import { abusePredefinedReasonsMap } from '@shared/core-utils/abuse' | 9 | import { abusePredefinedReasonsMap } from '@shared/core-utils/abuse' |
@@ -27,7 +27,7 @@ export class VideoReportComponent extends FormReactive implements OnInit { | |||
27 | private openedModal: NgbModalRef | 27 | private openedModal: NgbModalRef |
28 | 28 | ||
29 | constructor ( | 29 | constructor ( |
30 | protected formValidatorService: FormValidatorService, | 30 | protected formReactiveService: FormReactiveService, |
31 | private modalService: NgbModal, | 31 | private modalService: NgbModal, |
32 | private abuseService: AbuseService, | 32 | private abuseService: AbuseService, |
33 | private notifier: Notifier, | 33 | private notifier: Notifier, |
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 617408f2a..27dcf043a 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 | |||
@@ -2,7 +2,7 @@ import { forkJoin } from 'rxjs' | |||
2 | import { Component, EventEmitter, OnInit, Output, ViewChild } from '@angular/core' | 2 | import { Component, EventEmitter, OnInit, Output, ViewChild } from '@angular/core' |
3 | import { Notifier } from '@app/core' | 3 | import { Notifier } from '@app/core' |
4 | import { prepareIcu } from '@app/helpers' | 4 | import { prepareIcu } from '@app/helpers' |
5 | import { FormReactive, FormValidatorService } from '@app/shared/shared-forms' | 5 | import { FormReactive, FormReactiveService } from '@app/shared/shared-forms' |
6 | import { NgbModal } from '@ng-bootstrap/ng-bootstrap' | 6 | import { NgbModal } from '@ng-bootstrap/ng-bootstrap' |
7 | import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap/modal/modal-ref' | 7 | import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap/modal/modal-ref' |
8 | import { User } from '@shared/models' | 8 | import { User } from '@shared/models' |
@@ -25,7 +25,7 @@ export class UserBanModalComponent extends FormReactive implements OnInit { | |||
25 | modalMessage = '' | 25 | modalMessage = '' |
26 | 26 | ||
27 | constructor ( | 27 | constructor ( |
28 | protected formValidatorService: FormValidatorService, | 28 | protected formReactiveService: FormReactiveService, |
29 | private modalService: NgbModal, | 29 | private modalService: NgbModal, |
30 | private notifier: Notifier, | 30 | private notifier: Notifier, |
31 | private userAdminService: UserAdminService, | 31 | private userAdminService: UserAdminService, |
diff --git a/client/src/app/shared/shared-moderation/video-block.component.ts b/client/src/app/shared/shared-moderation/video-block.component.ts index f8b22a3f6..3ff53443a 100644 --- a/client/src/app/shared/shared-moderation/video-block.component.ts +++ b/client/src/app/shared/shared-moderation/video-block.component.ts | |||
@@ -1,7 +1,7 @@ | |||
1 | import { Component, EventEmitter, OnInit, Output, ViewChild } from '@angular/core' | 1 | import { Component, EventEmitter, OnInit, Output, ViewChild } from '@angular/core' |
2 | import { Notifier } from '@app/core' | 2 | import { Notifier } from '@app/core' |
3 | import { prepareIcu } from '@app/helpers' | 3 | import { prepareIcu } from '@app/helpers' |
4 | import { FormReactive, FormValidatorService } from '@app/shared/shared-forms' | 4 | import { FormReactive, FormReactiveService } from '@app/shared/shared-forms' |
5 | import { Video } from '@app/shared/shared-main' | 5 | import { Video } from '@app/shared/shared-main' |
6 | import { NgbModal } from '@ng-bootstrap/ng-bootstrap' | 6 | import { NgbModal } from '@ng-bootstrap/ng-bootstrap' |
7 | import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap/modal/modal-ref' | 7 | import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap/modal/modal-ref' |
@@ -25,7 +25,7 @@ export class VideoBlockComponent extends FormReactive implements OnInit { | |||
25 | private openedModal: NgbModalRef | 25 | private openedModal: NgbModalRef |
26 | 26 | ||
27 | constructor ( | 27 | constructor ( |
28 | protected formValidatorService: FormValidatorService, | 28 | protected formReactiveService: FormReactiveService, |
29 | private modalService: NgbModal, | 29 | private modalService: NgbModal, |
30 | private videoBlocklistService: VideoBlockService, | 30 | private videoBlocklistService: VideoBlockService, |
31 | private notifier: Notifier | 31 | private notifier: Notifier |
diff --git a/client/src/app/shared/shared-user-settings/user-interface-settings.component.ts b/client/src/app/shared/shared-user-settings/user-interface-settings.component.ts index 13e2e5424..c2c30d38b 100644 --- a/client/src/app/shared/shared-user-settings/user-interface-settings.component.ts +++ b/client/src/app/shared/shared-user-settings/user-interface-settings.component.ts | |||
@@ -1,7 +1,7 @@ | |||
1 | import { Subject, Subscription } from 'rxjs' | 1 | import { Subject, Subscription } from 'rxjs' |
2 | import { Component, Input, OnDestroy, OnInit } from '@angular/core' | 2 | import { Component, Input, OnDestroy, OnInit } from '@angular/core' |
3 | import { AuthService, Notifier, ServerService, ThemeService, UserService } from '@app/core' | 3 | import { AuthService, Notifier, ServerService, ThemeService, UserService } from '@app/core' |
4 | import { FormReactive, FormValidatorService } from '@app/shared/shared-forms' | 4 | import { FormReactive, FormReactiveService } from '@app/shared/shared-forms' |
5 | import { HTMLServerConfig, User, UserUpdateMe } from '@shared/models' | 5 | import { HTMLServerConfig, User, UserUpdateMe } from '@shared/models' |
6 | import { SelectOptionsItem } from 'src/types' | 6 | import { SelectOptionsItem } from 'src/types' |
7 | 7 | ||
@@ -22,7 +22,7 @@ export class UserInterfaceSettingsComponent extends FormReactive implements OnIn | |||
22 | private serverConfig: HTMLServerConfig | 22 | private serverConfig: HTMLServerConfig |
23 | 23 | ||
24 | constructor ( | 24 | constructor ( |
25 | protected formValidatorService: FormValidatorService, | 25 | protected formReactiveService: FormReactiveService, |
26 | private authService: AuthService, | 26 | private authService: AuthService, |
27 | private notifier: Notifier, | 27 | private notifier: Notifier, |
28 | private userService: UserService, | 28 | private userService: UserService, |
diff --git a/client/src/app/shared/shared-user-settings/user-video-settings.component.ts b/client/src/app/shared/shared-user-settings/user-video-settings.component.ts index 7d6b69469..af0870f12 100644 --- a/client/src/app/shared/shared-user-settings/user-video-settings.component.ts +++ b/client/src/app/shared/shared-user-settings/user-video-settings.component.ts | |||
@@ -3,7 +3,7 @@ import { Subject, Subscription } from 'rxjs' | |||
3 | import { first } from 'rxjs/operators' | 3 | import { first } from 'rxjs/operators' |
4 | import { Component, Input, OnDestroy, OnInit } from '@angular/core' | 4 | import { Component, Input, OnDestroy, OnInit } from '@angular/core' |
5 | import { AuthService, Notifier, ServerService, User, UserService } from '@app/core' | 5 | import { AuthService, Notifier, ServerService, User, UserService } from '@app/core' |
6 | import { FormReactive, FormValidatorService } from '@app/shared/shared-forms' | 6 | import { FormReactive, FormReactiveService } from '@app/shared/shared-forms' |
7 | import { UserUpdateMe } from '@shared/models' | 7 | import { UserUpdateMe } from '@shared/models' |
8 | import { NSFWPolicyType } from '@shared/models/videos/nsfw-policy.type' | 8 | import { NSFWPolicyType } from '@shared/models/videos/nsfw-policy.type' |
9 | 9 | ||
@@ -22,7 +22,7 @@ export class UserVideoSettingsComponent extends FormReactive implements OnInit, | |||
22 | formValuesWatcher: Subscription | 22 | formValuesWatcher: Subscription |
23 | 23 | ||
24 | constructor ( | 24 | constructor ( |
25 | protected formValidatorService: FormValidatorService, | 25 | protected formReactiveService: FormReactiveService, |
26 | private authService: AuthService, | 26 | private authService: AuthService, |
27 | private notifier: Notifier, | 27 | private notifier: Notifier, |
28 | private userService: UserService, | 28 | private userService: UserService, |
diff --git a/client/src/app/shared/shared-user-subscription/remote-subscribe.component.ts b/client/src/app/shared/shared-user-subscription/remote-subscribe.component.ts index 7bcfdd8aa..61bcd5345 100644 --- a/client/src/app/shared/shared-user-subscription/remote-subscribe.component.ts +++ b/client/src/app/shared/shared-user-subscription/remote-subscribe.component.ts | |||
@@ -1,6 +1,6 @@ | |||
1 | import { Component, Input, OnInit } from '@angular/core' | 1 | import { Component, Input, OnInit } from '@angular/core' |
2 | import { Notifier } from '@app/core' | 2 | import { Notifier } from '@app/core' |
3 | import { FormReactive, FormValidatorService } from '@app/shared/shared-forms' | 3 | import { FormReactive, FormReactiveService } from '@app/shared/shared-forms' |
4 | import { logger } from '@root-helpers/logger' | 4 | import { logger } from '@root-helpers/logger' |
5 | import { USER_HANDLE_VALIDATOR } from '../form-validators/user-validators' | 5 | import { USER_HANDLE_VALIDATOR } from '../form-validators/user-validators' |
6 | 6 | ||
@@ -15,7 +15,7 @@ export class RemoteSubscribeComponent extends FormReactive implements OnInit { | |||
15 | @Input() showHelp = false | 15 | @Input() showHelp = false |
16 | 16 | ||
17 | constructor ( | 17 | constructor ( |
18 | protected formValidatorService: FormValidatorService, | 18 | protected formReactiveService: FormReactiveService, |
19 | private notifier: Notifier | 19 | private notifier: Notifier |
20 | ) { | 20 | ) { |
21 | super() | 21 | super() |
diff --git a/client/src/app/shared/shared-users/index.ts b/client/src/app/shared/shared-users/index.ts index 8f90f2515..20e60486d 100644 --- a/client/src/app/shared/shared-users/index.ts +++ b/client/src/app/shared/shared-users/index.ts | |||
@@ -1,4 +1,5 @@ | |||
1 | export * from './user-admin.service' | 1 | export * from './user-admin.service' |
2 | export * from './user-signup.service' | 2 | export * from './user-signup.service' |
3 | export * from './two-factor.service' | ||
3 | 4 | ||
4 | export * from './shared-users.module' | 5 | 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 index 2a1dadf20..5a1675dc9 100644 --- a/client/src/app/shared/shared-users/shared-users.module.ts +++ b/client/src/app/shared/shared-users/shared-users.module.ts | |||
@@ -1,6 +1,7 @@ | |||
1 | 1 | ||
2 | import { NgModule } from '@angular/core' | 2 | import { NgModule } from '@angular/core' |
3 | import { SharedMainModule } from '../shared-main/shared-main.module' | 3 | import { SharedMainModule } from '../shared-main/shared-main.module' |
4 | import { TwoFactorService } from './two-factor.service' | ||
4 | import { UserAdminService } from './user-admin.service' | 5 | import { UserAdminService } from './user-admin.service' |
5 | import { UserSignupService } from './user-signup.service' | 6 | import { UserSignupService } from './user-signup.service' |
6 | 7 | ||
@@ -15,7 +16,8 @@ import { UserSignupService } from './user-signup.service' | |||
15 | 16 | ||
16 | providers: [ | 17 | providers: [ |
17 | UserSignupService, | 18 | UserSignupService, |
18 | UserAdminService | 19 | UserAdminService, |
20 | TwoFactorService | ||
19 | ] | 21 | ] |
20 | }) | 22 | }) |
21 | export class SharedUsersModule { } | 23 | export class SharedUsersModule { } |
diff --git a/client/src/app/shared/shared-users/two-factor.service.ts b/client/src/app/shared/shared-users/two-factor.service.ts new file mode 100644 index 000000000..9ff916f15 --- /dev/null +++ b/client/src/app/shared/shared-users/two-factor.service.ts | |||
@@ -0,0 +1,52 @@ | |||
1 | import { catchError } 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 { TwoFactorEnableResult } from '@shared/models' | ||
6 | |||
7 | @Injectable() | ||
8 | export class TwoFactorService { | ||
9 | constructor ( | ||
10 | private authHttp: HttpClient, | ||
11 | private restExtractor: RestExtractor | ||
12 | ) { } | ||
13 | |||
14 | // --------------------------------------------------------------------------- | ||
15 | |||
16 | requestTwoFactor (options: { | ||
17 | userId: number | ||
18 | currentPassword: string | ||
19 | }) { | ||
20 | const { userId, currentPassword } = options | ||
21 | |||
22 | const url = UserService.BASE_USERS_URL + userId + '/two-factor/request' | ||
23 | |||
24 | return this.authHttp.post<TwoFactorEnableResult>(url, { currentPassword }) | ||
25 | .pipe(catchError(err => this.restExtractor.handleError(err))) | ||
26 | } | ||
27 | |||
28 | confirmTwoFactorRequest (options: { | ||
29 | userId: number | ||
30 | requestToken: string | ||
31 | otpToken: string | ||
32 | }) { | ||
33 | const { userId, requestToken, otpToken } = options | ||
34 | |||
35 | const url = UserService.BASE_USERS_URL + userId + '/two-factor/confirm-request' | ||
36 | |||
37 | return this.authHttp.post(url, { requestToken, otpToken }) | ||
38 | .pipe(catchError(err => this.restExtractor.handleError(err))) | ||
39 | } | ||
40 | |||
41 | disableTwoFactor (options: { | ||
42 | userId: number | ||
43 | currentPassword?: string | ||
44 | }) { | ||
45 | const { userId, currentPassword } = options | ||
46 | |||
47 | const url = UserService.BASE_USERS_URL + userId + '/two-factor/disable' | ||
48 | |||
49 | return this.authHttp.post(url, { currentPassword }) | ||
50 | .pipe(catchError(err => this.restExtractor.handleError(err))) | ||
51 | } | ||
52 | } | ||
diff --git a/client/src/app/shared/shared-video-playlist/video-add-to-playlist.component.ts b/client/src/app/shared/shared-video-playlist/video-add-to-playlist.component.ts index e019fdd26..f81de7c6b 100644 --- a/client/src/app/shared/shared-video-playlist/video-add-to-playlist.component.ts +++ b/client/src/app/shared/shared-video-playlist/video-add-to-playlist.component.ts | |||
@@ -3,7 +3,7 @@ import { Subject, Subscription } from 'rxjs' | |||
3 | import { debounceTime, filter } from 'rxjs/operators' | 3 | import { debounceTime, filter } from 'rxjs/operators' |
4 | import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnChanges, OnDestroy, OnInit, SimpleChanges } from '@angular/core' | 4 | import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnChanges, OnDestroy, OnInit, SimpleChanges } from '@angular/core' |
5 | import { AuthService, DisableForReuseHook, Notifier } from '@app/core' | 5 | import { AuthService, DisableForReuseHook, Notifier } from '@app/core' |
6 | import { FormReactive, FormValidatorService } from '@app/shared/shared-forms' | 6 | import { FormReactive, FormReactiveService } from '@app/shared/shared-forms' |
7 | import { secondsToTime } from '@shared/core-utils' | 7 | import { secondsToTime } from '@shared/core-utils' |
8 | import { | 8 | import { |
9 | Video, | 9 | Video, |
@@ -59,7 +59,7 @@ export class VideoAddToPlaylistComponent extends FormReactive implements OnInit, | |||
59 | private pendingAddId: number | 59 | private pendingAddId: number |
60 | 60 | ||
61 | constructor ( | 61 | constructor ( |
62 | protected formValidatorService: FormValidatorService, | 62 | protected formReactiveService: FormReactiveService, |
63 | private authService: AuthService, | 63 | private authService: AuthService, |
64 | private notifier: Notifier, | 64 | private notifier: Notifier, |
65 | private videoPlaylistService: VideoPlaylistService, | 65 | private videoPlaylistService: VideoPlaylistService, |