Skip to content

Commit 6644dd8

Browse files
authored
Merge branch 'master' into master
2 parents fa94364 + 9b76d1e commit 6644dd8

File tree

16 files changed

+578
-7
lines changed

16 files changed

+578
-7
lines changed

angular/src/app/app-routing.module.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { AboutComponent } from './about/about.component';
77
import { UsersComponent } from './users/users.component';
88
import { TenantsComponent } from './tenants/tenants.component';
99
import { RolesComponent } from 'app/roles/roles.component';
10+
import { ChangePasswordComponent } from './users/change-password/change-password.component';
1011

1112
@NgModule({
1213
imports: [
@@ -19,7 +20,8 @@ import { RolesComponent } from 'app/roles/roles.component';
1920
{ path: 'users', component: UsersComponent, data: { permission: 'Pages.Users' }, canActivate: [AppRouteGuard] },
2021
{ path: 'roles', component: RolesComponent, data: { permission: 'Pages.Roles' }, canActivate: [AppRouteGuard] },
2122
{ path: 'tenants', component: TenantsComponent, data: { permission: 'Pages.Tenants' }, canActivate: [AppRouteGuard] },
22-
{ path: 'about', component: AboutComponent }
23+
{ path: 'about', component: AboutComponent },
24+
{ path: 'update-password', component: ChangePasswordComponent }
2325
]
2426
}
2527
])

angular/src/app/app.module.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { NgModule } from '@angular/core';
22
import { CommonModule } from '@angular/common';
3-
import { FormsModule } from '@angular/forms';
3+
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
44
import { JsonpModule } from '@angular/http';
55
import { HttpClientModule } from '@angular/common/http';
66

@@ -35,6 +35,8 @@ import { EditRoleDialogComponent } from './roles/edit-role/edit-role-dialog.comp
3535
import { UsersComponent } from '@app/users/users.component';
3636
import { CreateUserDialogComponent } from '@app/users/create-user/create-user-dialog.component';
3737
import { EditUserDialogComponent } from '@app/users/edit-user/edit-user-dialog.component';
38+
import { ChangePasswordComponent } from './users/change-password/change-password.component';
39+
import { ResetPasswordDialogComponent } from './users/reset-password/reset-password.component';
3840

3941
@NgModule({
4042
declarations: [
@@ -58,11 +60,14 @@ import { EditUserDialogComponent } from '@app/users/edit-user/edit-user-dialog.c
5860
// users
5961
UsersComponent,
6062
CreateUserDialogComponent,
61-
EditUserDialogComponent
63+
EditUserDialogComponent,
64+
ChangePasswordComponent,
65+
ResetPasswordDialogComponent
6266
],
6367
imports: [
6468
CommonModule,
6569
FormsModule,
70+
ReactiveFormsModule,
6671
HttpClientModule,
6772
JsonpModule,
6873
ModalModule.forRoot(),
@@ -82,7 +87,8 @@ import { EditUserDialogComponent } from '@app/users/edit-user/edit-user-dialog.c
8287
EditRoleDialogComponent,
8388
// users
8489
CreateUserDialogComponent,
85-
EditUserDialogComponent
90+
EditUserDialogComponent,
91+
ResetPasswordDialogComponent
8692
]
8793
})
8894
export class AppModule {}

angular/src/app/layout/sidebar-user-area.component.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
<div class="btn-group user-helper-dropdown">
99
<i class="material-icons" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true">keyboard_arrow_down</i>
1010
<ul class="dropdown-menu pull-right">
11+
<li><a [routerLink]="['/app/update-password']"><i class="material-icons">lock</i>Update Password</a></li>
1112
<li><a (click)="logout()"><i class="material-icons">input</i>{{ 'Logout' | localize }}</a></li>
1213
</ul>
1314
</div>
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
<div class="row clearfix" [@routerTransition]>
2+
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
3+
<div class="card main-content">
4+
<div class="header">
5+
<h2>{{ "UpdatePassword" | localize }}</h2>
6+
</div>
7+
<div class="body table-responsive" #body>
8+
<form novalidate (ngSubmit)="updatePassword(parentFormGroup.value)">
9+
<div [formGroup]="parentFormGroup">
10+
<div class="row">
11+
<div class="col-md-6">
12+
<mat-form-field>
13+
<input matInput id="currentPassword" type="password" name="CurrentPassword" formControlName="currentPassword"
14+
[placeholder]="'Current Password' | localize"
15+
required minlength="2" maxlength="32"
16+
/>
17+
</mat-form-field>
18+
</div>
19+
</div>
20+
<div class="row">
21+
<div class="col-md-6" [formGroup]="passwordsFormGroup" formGroupName="passwords">
22+
<mat-form-field>
23+
<input matInput id="newPassword" formControlName="newPassword" type="password" name="NewPassword"
24+
[placeholder]="'New Password' | localize"
25+
required minlength="2" maxlength="32"
26+
/>
27+
<mat-error *ngIf="passwordsFormGroup.controls.newPassword.errors">
28+
Passwords must be at least 8 characters, contain a lowercase, uppercase, and number
29+
</mat-error>
30+
</mat-form-field>
31+
</div>
32+
</div>
33+
<div class="row">
34+
<div class="col-md-6" formGroupName="passwords">
35+
<mat-form-field>
36+
<input matInput id="repeatNewPassword" formControlName="repeatNewPassword" type="password" name="RepeatNewPassword"
37+
[placeholder]="'Confirm New Password' | localize" [errorStateMatcher]="equalMatcher"
38+
required minlength="2" maxlength="32"
39+
/>
40+
<mat-error *ngIf="passwordsFormGroup.errors &&
41+
passwordsFormGroup.errors.areEqual &&
42+
passwordsFormGroup.controls.newPassword.touched
43+
">
44+
Passwords do not match
45+
</mat-error>
46+
</mat-form-field>
47+
</div>
48+
</div>
49+
<button mat-flat-button type="submit" flex="15" color="primary"
50+
[disabled]="!parentFormGroup.valid || isLoading">
51+
{{ "Save" | localize }}
52+
<i class="fa fa-spin fa-spinner" *ngIf="isLoading"></i>
53+
</button>
54+
</div>
55+
</form>
56+
</div>
57+
</div>
58+
</div>
59+
</div>
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
import { Component, OnInit, Injector, ViewChild, ElementRef, AfterViewInit } from '@angular/core';
2+
import { appModuleAnimation } from '@shared/animations/routerTransition';
3+
import { AppComponentBase } from '@shared/app-component-base';
4+
import { ChangePasswordDto, UserServiceProxy } from '@shared/service-proxies/service-proxies';
5+
import { Router } from '@angular/router';
6+
import { FormGroup, FormControl, Validators, AbstractControl, ValidationErrors, FormGroupDirective, NgForm } from '@angular/forms';
7+
import { finalize } from 'rxjs/operators';
8+
import { ErrorStateMatcher } from '@angular/material';
9+
10+
export class FormGroupErrorStateMatcher implements ErrorStateMatcher {
11+
constructor(private formGroup: FormGroup) { }
12+
13+
public isErrorState(control: FormControl | null, form: FormGroupDirective | NgForm | null): boolean {
14+
return control && control.dirty && control.touched && this.formGroup && this.formGroup.errors && this.formGroup.errors.areEqual;
15+
}
16+
}
17+
18+
@Component({
19+
animations: [appModuleAnimation()],
20+
templateUrl: './change-password.component.html'
21+
})
22+
export class ChangePasswordComponent extends AppComponentBase implements OnInit {
23+
public parentFormGroup: FormGroup;
24+
public passwordsFormGroup: FormGroup;
25+
public isLoading: boolean;
26+
public equalMatcher: FormGroupErrorStateMatcher;
27+
28+
private static areEqual(c: AbstractControl): ValidationErrors | null {
29+
const keys: string[] = Object.keys(c.value);
30+
for (const i in keys) {
31+
if (i !== '0' && c.value[keys[+i - 1]] !== c.value[keys[i]]) {
32+
return { areEqual: true };
33+
}
34+
}
35+
}
36+
37+
public constructor(
38+
injector: Injector,
39+
private userServiceProxy: UserServiceProxy,
40+
private router: Router
41+
) {
42+
super(injector);
43+
}
44+
45+
public ngOnInit() {
46+
this.isLoading = true;
47+
48+
this.passwordsFormGroup = new FormGroup({
49+
'newPassword': new FormControl('', [
50+
Validators.required,
51+
Validators.pattern('(?=^.{8,}$)(?=.*\\d)(?=.*[a-z])(?=.*[A-Z])(?!.*\\s)[0-9a-zA-Z!@#$%^&*()]*$') ]),
52+
'repeatNewPassword': new FormControl('', [ Validators.required ])
53+
}, ChangePasswordComponent.areEqual);
54+
55+
this.parentFormGroup = new FormGroup({
56+
'currentPassword': new FormControl('', [ Validators.required ]),
57+
'passwords': this.passwordsFormGroup
58+
});
59+
60+
this.equalMatcher = new FormGroupErrorStateMatcher(this.passwordsFormGroup);
61+
62+
this.doneLoading();
63+
}
64+
65+
public updatePassword(formValue: any) {
66+
const changePasswordDto = new ChangePasswordDto();
67+
changePasswordDto.currentPassword = formValue.currentPassword;
68+
changePasswordDto.newPassword = formValue.passwords.newPassword;
69+
70+
this.isLoading = true;
71+
this.userServiceProxy.changePassword(changePasswordDto)
72+
.pipe(
73+
finalize(() => {
74+
this.doneLoading();
75+
})
76+
)
77+
.subscribe(success => {
78+
if (success) {
79+
abp.message.success('Password changed successfully', 'Success');
80+
this.router.navigate(['/']);
81+
}
82+
});
83+
}
84+
85+
private doneLoading(): void {
86+
this.isLoading = false;
87+
}
88+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<form autocomplete="off" #resetPasswordModal="ngForm" (ngSubmit)="resetPassword()">
2+
<h1 mat-dialog-title>{{ "ResetPassword" | localize }}</h1>
3+
<mat-dialog-content style="width:800px;height:250px;">
4+
<div class="row">
5+
<div class="col-sm-12">
6+
<mat-form-field>
7+
<input matInput id="adminPassword" type="password" [(ngModel)]="resetPasswordDto.adminPassword"
8+
name="AdminPassword" [placeholder]="'AdminPassword' | localize" required />
9+
<mat-hint>1. Enter your administrator password</mat-hint>
10+
</mat-form-field>
11+
</div>
12+
<div class="col-sm-12">
13+
<mat-form-field>
14+
<input matInput id="newPassword" readonly [ngModel]="resetPasswordDto.newPassword" type="text" name="NewPassword" />
15+
<mat-hint>2. Copy this random password so you can send it to the user</mat-hint>
16+
</mat-form-field>
17+
</div>
18+
</div>
19+
</mat-dialog-content>
20+
<div mat-dialog-actions align="end">
21+
<button mat-button type="button" [disabled]="loading" (click)="close()">
22+
{{ "Cancel" | localize }}
23+
</button>
24+
<button mat-flat-button type="submit" flex="15" color="primary" [disabled]="!resetPasswordModal.form.valid || loading">
25+
{{ "Save" | localize }}
26+
</button>
27+
</div>
28+
</form>
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import { Component, OnInit, Optional, Injector, Inject } from '@angular/core';
2+
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material';
3+
import { AppComponentBase } from '@shared/app-component-base';
4+
import { finalize } from 'rxjs/operators';
5+
import {
6+
UserServiceProxy,
7+
ResetPasswordDto
8+
} from '@shared/service-proxies/service-proxies';
9+
10+
@Component({
11+
selector: 'app-reset-password',
12+
templateUrl: './reset-password.component.html'
13+
})
14+
export class ResetPasswordDialogComponent extends AppComponentBase
15+
implements OnInit {
16+
public isLoading = false;
17+
public resetPasswordDto: ResetPasswordDto;
18+
19+
constructor(
20+
injector: Injector,
21+
private _userService: UserServiceProxy,
22+
private _dialogRef: MatDialogRef<ResetPasswordDialogComponent>,
23+
@Optional() @Inject(MAT_DIALOG_DATA) private _userId: number
24+
) {
25+
super(injector);
26+
}
27+
28+
ngOnInit() {
29+
this.isLoading = true;
30+
this.resetPasswordDto = new ResetPasswordDto();
31+
this.resetPasswordDto.userId = this._userId;
32+
this.resetPasswordDto.newPassword = Math.random()
33+
.toString(36)
34+
.substr(2, 10);
35+
this.isLoading = false;
36+
}
37+
38+
public resetPassword(): void {
39+
this.isLoading = true;
40+
this._userService
41+
.resetPassword(this.resetPasswordDto)
42+
.pipe(
43+
finalize(() => {
44+
this.isLoading = false;
45+
})
46+
)
47+
.subscribe(() => {
48+
this.notify.info('Password Reset');
49+
this.close(true);
50+
});
51+
}
52+
53+
close(result: any): void {
54+
this._dialogRef.close(result);
55+
}
56+
}

angular/src/app/users/users.component.html

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,10 @@ <h2>{{ "Users" | localize }}</h2>
8585
<mat-icon>delete</mat-icon>
8686
<span>{{ "Delete" | localize }}</span>
8787
</button>
88+
<button mat-menu-item (click)="resetPassword(user)">
89+
<mat-icon>lock</mat-icon>
90+
<span>{{ "Reset Password" | localize }}</span>
91+
</button>
8892
</mat-menu>
8993
</td>
9094
</tr>

angular/src/app/users/users.component.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import { PagedListingComponentBase, PagedRequestDto } from 'shared/paged-listing
66
import { UserServiceProxy, UserDto, PagedResultDtoOfUserDto } from '@shared/service-proxies/service-proxies';
77
import { CreateUserDialogComponent } from './create-user/create-user-dialog.component';
88
import { EditUserDialogComponent } from './edit-user/edit-user-dialog.component';
9+
import { Moment } from 'moment';
10+
import { ResetPasswordDialogComponent } from './reset-password/reset-password.component';
911

1012
class PagedUsersRequestDto extends PagedRequestDto {
1113
keyword: string;
@@ -80,7 +82,17 @@ export class UsersComponent extends PagedListingComponentBase<UserDto> {
8082
this.showCreateOrEditUserDialog(user.id);
8183
}
8284

83-
showCreateOrEditUserDialog(id?: number): void {
85+
public resetPassword(user: UserDto): void {
86+
this.showResetPasswordUserDialog(user.id);
87+
}
88+
89+
private showResetPasswordUserDialog(userId?: number): void {
90+
this._dialog.open(ResetPasswordDialogComponent, {
91+
data: userId
92+
});
93+
}
94+
95+
private showCreateOrEditUserDialog(id?: number): void {
8496
let createOrEditUserDialog;
8597
if (id === undefined || id <= 0) {
8698
createOrEditUserDialog = this._dialog.open(CreateUserDialogComponent);

angular/src/shared/core.less

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -244,4 +244,9 @@ topbar-languageswitch {
244244

245245
.action-button {
246246
top: -9px;
247-
}
247+
}
248+
249+
// force all material design form fields to be 100% wide
250+
.mat-form-field {
251+
display: block !important;
252+
}

0 commit comments

Comments
 (0)