Strongest password policy
This commit is contained in:
committed by
Jose Alberto Hernandez
parent
9b17a6c4f8
commit
8fdf8fb10c
@@ -45,7 +45,7 @@ export class ChartOfAccountsComponent implements AfterViewInit, OnInit {
|
||||
/** Selected GL Account. */
|
||||
glAccount: GLAccountNode;
|
||||
/** Flag to check if tree is expanded or collapsed. */
|
||||
isTreeExpanded: boolean = true;
|
||||
isTreeExpanded = true;
|
||||
|
||||
/** Paginator for chart of accounts table. */
|
||||
@ViewChild(MatPaginator, { static: true }) paginator: MatPaginator;
|
||||
@@ -182,9 +182,9 @@ export class ChartOfAccountsComponent implements AfterViewInit, OnInit {
|
||||
this.router.navigate(['/accounting']);
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* Expand and Collapse the tree
|
||||
*/
|
||||
*/
|
||||
toggleExpandCollapse() {
|
||||
this.isTreeExpanded = this.treeControlService.toggleExpandCollapse(this.nestedTreeControl, this.isTreeExpanded);
|
||||
}
|
||||
|
||||
+40
-15
@@ -12,7 +12,8 @@
|
||||
<mat-label> {{"labels.inputs.User Name" | translate}} </mat-label>
|
||||
<input matInput required formControlName="username">
|
||||
<mat-error *ngIf="createSelfServiceForm.controls.username.hasError('required')">
|
||||
{{"labels.inputs.User Name" | translate}} {{"labels.commons.is" | translate}} <strong>{{"labels.commons.required" | translate}}</strong>
|
||||
{{"labels.inputs.User Name" | translate}} {{"labels.commons.is" | translate}}
|
||||
<strong>{{"labels.commons.required" | translate}}</strong>
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
|
||||
@@ -20,7 +21,8 @@
|
||||
<mat-label> {{"labels.inputs.First Name" | translate}} </mat-label>
|
||||
<input matInput required formControlName="firstname">
|
||||
<mat-error *ngIf="createSelfServiceForm.controls.firstname.hasError('required')">
|
||||
{{"labels.inputs.First Name" | translate}} {{"labels.commons.is" | translate}} <strong>{{"labels.commons.required" | translate}}</strong>
|
||||
{{"labels.inputs.First Name" | translate}} {{"labels.commons.is" | translate}}
|
||||
<strong>{{"labels.commons.required" | translate}}</strong>
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
|
||||
@@ -28,7 +30,8 @@
|
||||
<mat-label> {{"labels.inputs.Last Name" | translate}} </mat-label>
|
||||
<input matInput required formControlName="lastname">
|
||||
<mat-error *ngIf="createSelfServiceForm.controls.lastname.hasError('required')">
|
||||
{{"labels.inputs.Last Name" | translate}} {{"labels.commons.is" | translate}} <strong>{{"labels.commons.required" | translate}}</strong>
|
||||
{{"labels.inputs.Last Name" | translate}} {{"labels.commons.is" | translate}}
|
||||
<strong>{{"labels.commons.required" | translate}}</strong>
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
|
||||
@@ -36,10 +39,12 @@
|
||||
<mat-label> {{"labels.inputs.Email" | translate}} </mat-label>
|
||||
<input matInput required formControlName="email">
|
||||
<mat-error *ngIf="createSelfServiceForm.controls.email.hasError('required')">
|
||||
{{"labels.inputs.Email" | translate}} {{"labels.commons.is" | translate}} <strong>{{"labels.commons.required" | translate}}</strong>
|
||||
{{"labels.inputs.Email" | translate}} {{"labels.commons.is" | translate}}
|
||||
<strong>{{"labels.commons.required" | translate}}</strong>
|
||||
</mat-error>
|
||||
<mat-error *ngIf="createSelfServiceForm.controls.email.hasError('email')">
|
||||
{{"labels.inputs.Email should be a" | translate}} <strong>{{"labels.inputs.valid" | translate}}</strong> {{"labels.inputs.email" | translate}}.
|
||||
{{"labels.inputs.Email should be a" | translate}} <strong>{{"labels.inputs.valid" |
|
||||
translate}}</strong> {{"labels.inputs.email" | translate}}.
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
|
||||
@@ -53,15 +58,30 @@
|
||||
{{"labels.inputs.Override Password Expire Policy" | translate}} ?
|
||||
</mat-checkbox>
|
||||
|
||||
<i class="fa fa-question-circle fa fa-align-left" title="Determines if passwords do not expire for this user even if global configuration for password expiry is enabled"></i>
|
||||
<i class="fa fa-question-circle fa fa-align-left"
|
||||
title="Determines if passwords do not expire for this user even if global configuration for password expiry is enabled"></i>
|
||||
|
||||
</div>
|
||||
|
||||
<mat-form-field fxFlex="48%" *ngIf="!hidePasswordField">
|
||||
<mat-label> {{"labels.inputs.Password" | translate}} </mat-label>
|
||||
<input matInput required formControlName="password" type="password">
|
||||
<mat-label>{{ 'labels.inputs.Password' | translate }}</mat-label>
|
||||
<input matInput formControlName="password" type="password">
|
||||
<mat-error *ngIf="createSelfServiceForm.controls.password.hasError('required')">
|
||||
{{"labels.inputs.Password" | translate}} {{"labels.commons.is" | translate}} <strong>{{"labels.commons.required" | translate}}</strong>
|
||||
{{ 'labels.inputs.Password' | translate }} <strong>{{'labels.commons.is required' | translate }}</strong>
|
||||
</mat-error>
|
||||
<mat-error *ngIf="createSelfServiceForm.controls.password.hasError('minlength')">
|
||||
{{'labels.commons.Password should be at least' | translate }} <strong>{{'labels.commons.8 characters long' | translate }}</strong>
|
||||
</mat-error>
|
||||
<mat-error *ngIf="createSelfServiceForm.controls.password.hasError('maxlength')">
|
||||
{{'labels.commons.Password should not be more than' | translate }}
|
||||
<strong>{{'labels.commons.50 characters long' | translate }}</strong>
|
||||
</mat-error>
|
||||
<mat-error
|
||||
*ngIf="createSelfServiceForm.controls.password.hasError('pattern') && !(createSelfServiceForm.controls.password.hasError('minlength') || createSelfServiceForm.controls.password.hasError('maxlength'))">
|
||||
{{'labels.commons.Password should include a' | translate }}
|
||||
<strong>{{'labels.commons.numeral' | translate }}</strong> {{'labels.commons.and at' | translate }}
|
||||
<strong>{{'labels.commons.least one uppercase' | translate }}</strong>
|
||||
,<strong>{{'labels.commons.lowercase and special character' | translate }}</strong>
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
|
||||
@@ -69,10 +89,12 @@
|
||||
<mat-label> {{"labels.inputs.Repeat Password" | translate}} </mat-label>
|
||||
<input matInput required formControlName="repeatPassword" type="password">
|
||||
<mat-error *ngIf="createSelfServiceForm.controls.repeatPassword.hasError('required')">
|
||||
{{"labels.inputs.Repeat password" | translate}} {{"labels.commons.is" | translate}} <strong>{{"labels.commons.required" | translate}}</strong>
|
||||
{{"labels.inputs.Repeat password" | translate}} {{"labels.commons.is" | translate}}
|
||||
<strong>{{"labels.commons.required" | translate}}</strong>
|
||||
</mat-error>
|
||||
<mat-error *ngIf="createSelfServiceForm.controls.repeatPassword.hasError('notequal')">
|
||||
{{"labels.inputs.Passwords should be" | translate}} <strong>{{"labels.inputs.matched" | translate}}</strong>
|
||||
{{"labels.inputs.Passwords should be" | translate}} <strong>{{"labels.inputs.matched" |
|
||||
translate}}</strong>
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
|
||||
@@ -84,9 +106,12 @@
|
||||
</div>
|
||||
|
||||
<mat-card-actions fxLayoutGap="5px" fxLayout="row" fxLayout.xs="column" fxLayoutAlign="center">
|
||||
<button type="button" mat-raised-button [routerLink]="['../../']">{{"labels.buttons.Cancel" | translate}}</button>
|
||||
<button mat-raised-button color="primary" [disabled]="!createSelfServiceForm.valid">{{"labels.buttons.Activate Self Service User" | translate}}</button>
|
||||
<i class="fa fa-question-circle fa-2x" title="Activate Self Service User Help"></i>
|
||||
<button type="button" mat-raised-button [routerLink]="['../../']">{{"labels.buttons.Cancel" |
|
||||
translate}}</button>
|
||||
<button mat-raised-button color="primary"
|
||||
[disabled]="!createSelfServiceForm.valid">{{"labels.buttons.Activate Self Service User" |
|
||||
translate}}</button>
|
||||
<i class="fa fa-question-circle fa-2x" title="Activate Self Service User Help"></i>
|
||||
</mat-card-actions>
|
||||
|
||||
</mat-card-content>
|
||||
@@ -95,4 +120,4 @@
|
||||
|
||||
</mat-card>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
+5
-23
@@ -6,6 +6,7 @@ import { Subscription } from 'rxjs';
|
||||
|
||||
/** Client Services. */
|
||||
import { ClientsService } from 'app/clients/clients.service';
|
||||
import { PasswordsUtility } from 'app/core/utils/passwords-utility';
|
||||
|
||||
@Component({
|
||||
selector: 'mifosx-create-self-service-user',
|
||||
@@ -21,7 +22,8 @@ export class CreateSelfServiceUserComponent implements OnInit {
|
||||
constructor(private formBuilder: UntypedFormBuilder,
|
||||
private route: ActivatedRoute,
|
||||
private clientService: ClientsService,
|
||||
private router: Router) {
|
||||
private router: Router,
|
||||
private passwordsUtility: PasswordsUtility) {
|
||||
this.route.data.subscribe((data: { clientActionData: any}) => {
|
||||
this.clientData = data.clientActionData;
|
||||
});
|
||||
@@ -43,8 +45,8 @@ export class CreateSelfServiceUserComponent implements OnInit {
|
||||
this.createSelfServiceForm.removeControl('repeatPassword');
|
||||
} else {
|
||||
this.hidePasswordField = false;
|
||||
this.createSelfServiceForm.addControl('password', new UntypedFormControl('', [Validators.required]));
|
||||
this.createSelfServiceForm.addControl('repeatPassword', new UntypedFormControl('', [Validators.required, this.confirmPassword('password')]));
|
||||
this.createSelfServiceForm.addControl('password', new UntypedFormControl('', this.passwordsUtility.getPasswordValidators()));
|
||||
this.createSelfServiceForm.addControl('repeatPassword', new UntypedFormControl('', [Validators.required, this.passwordsUtility.confirmPassword('password')]));
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -83,24 +85,4 @@ export class CreateSelfServiceUserComponent implements OnInit {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirm Change Password of Users
|
||||
* @param controlNameToCompare Form Control Name to be compared.
|
||||
*/
|
||||
confirmPassword(controlNameToCompare: string): ValidatorFn {
|
||||
return (c: AbstractControl): ValidationErrors|null => {
|
||||
if (c.value == null || c.value.length === 0) {
|
||||
return null;
|
||||
}
|
||||
const controlToCompare = c.root.get(controlNameToCompare);
|
||||
if (controlToCompare) {
|
||||
const subscription: Subscription = controlToCompare.valueChanges.subscribe(() => {
|
||||
c.updateValueAndValidity();
|
||||
subscription.unsubscribe();
|
||||
});
|
||||
}
|
||||
return controlToCompare && controlToCompare.value !== c.value ? {'notequal': true} : null;
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -121,7 +121,9 @@
|
||||
|
||||
<ng-container matColumnDef="Loan Account">
|
||||
<th mat-header-cell *matHeaderCellDef> {{"labels.inputs.Loan Product" | translate}} </th>
|
||||
<td mat-cell *matCellDef="let element"> {{element.productName}} </td>
|
||||
<td mat-cell *matCellDef="let element">
|
||||
<mifosx-long-text textValue="{{element.productName}}" chars="35"></mifosx-long-text>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="Original Loan">
|
||||
@@ -195,7 +197,9 @@
|
||||
|
||||
<ng-container matColumnDef="Loan Account">
|
||||
<th mat-header-cell *matHeaderCellDef> {{"labels.inputs.Loan Product" | translate}} </th>
|
||||
<td mat-cell *matCellDef="let element"> {{element.productName}} </td>
|
||||
<td mat-cell *matCellDef="let element">
|
||||
<mifosx-long-text textValue="{{element.productName}}" chars="35"></mifosx-long-text>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="Original Loan">
|
||||
@@ -260,7 +264,9 @@
|
||||
|
||||
<ng-container matColumnDef="Saving Account">
|
||||
<th mat-header-cell *matHeaderCellDef> {{"labels.inputs.Savings Product" | translate}} </th>
|
||||
<td mat-cell *matCellDef="let element"> {{element.productName}} </td>
|
||||
<td mat-cell *matCellDef="let element">
|
||||
<mifosx-long-text textValue="{{element.productName}}" chars="35"></mifosx-long-text>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="Last Active">
|
||||
@@ -320,7 +326,9 @@
|
||||
|
||||
<ng-container matColumnDef="Saving Account">
|
||||
<th mat-header-cell *matHeaderCellDef> {{"labels.inputs.Saving Product" | translate}} </th>
|
||||
<td mat-cell *matCellDef="let element"> {{element.productName}} </td>
|
||||
<td mat-cell *matCellDef="let element">
|
||||
<mifosx-long-text textValue="{{element.productName}}" chars="35"></mifosx-long-text>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="Closed Date">
|
||||
@@ -415,7 +423,9 @@
|
||||
|
||||
<ng-container matColumnDef="Saving Account">
|
||||
<th mat-header-cell *matHeaderCellDef> {{"labels.inputs.Fixed Deposit Product" | translate}} </th>
|
||||
<td mat-cell *matCellDef="let element"> {{element.productName}} </td>
|
||||
<td mat-cell *matCellDef="let element">
|
||||
<mifosx-long-text textValue="{{element.productName}}" chars="35"></mifosx-long-text>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="Closed Date">
|
||||
@@ -458,7 +468,9 @@
|
||||
|
||||
<ng-container matColumnDef="Saving Account">
|
||||
<th mat-header-cell *matHeaderCellDef> {{"labels.inputs.Recurring Deposit Product" | translate}} </th>
|
||||
<td mat-cell *matCellDef="let element"> {{element.productName}} </td>
|
||||
<td mat-cell *matCellDef="let element">
|
||||
<mifosx-long-text textValue="{{element.productName}}" chars="35"></mifosx-long-text>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="Last Active">
|
||||
@@ -515,7 +527,9 @@
|
||||
|
||||
<ng-container matColumnDef="Saving Account">
|
||||
<th mat-header-cell *matHeaderCellDef> {{"labels.inputs.Recurring Deposit Product" | translate}} </th>
|
||||
<td mat-cell *matCellDef="let element"> {{element.productName}} </td>
|
||||
<td mat-cell *matCellDef="let element">
|
||||
<mifosx-long-text textValue="{{element.productName}}" chars="35"></mifosx-long-text>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="Closed Date">
|
||||
@@ -556,7 +570,9 @@
|
||||
|
||||
<ng-container matColumnDef="Share Account">
|
||||
<th mat-header-cell *matHeaderCellDef> {{"labels.inputs.Share Product" | translate}} </th>
|
||||
<td mat-cell *matCellDef="let element"> {{element.productName}} </td>
|
||||
<td mat-cell *matCellDef="let element">
|
||||
<mifosx-long-text textValue="{{element.productName}}" chars="35"></mifosx-long-text>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="Approved Shares">
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { AbstractControl, ValidationErrors, ValidatorFn, Validators } from '@angular/forms';
|
||||
import { Subscription } from 'rxjs';
|
||||
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
|
||||
export class PasswordsUtility {
|
||||
// password regex pattern
|
||||
public static PASSWORD_REGEX = '^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9])(?=.*[#$@$!%*?&])[A-Za-z\d#$@$!%*?&].{8,}$';
|
||||
|
||||
public getPasswordValidators(): ValidatorFn[] {
|
||||
return [Validators.required, Validators.pattern(PasswordsUtility.PASSWORD_REGEX), Validators.maxLength(50), Validators.minLength(8)];
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirm Change Password of Users
|
||||
* @param controlNameToCompare Form Control Name to be compared.
|
||||
*/
|
||||
public confirmPassword(controlNameToCompare: string): ValidatorFn {
|
||||
return (c: AbstractControl): ValidationErrors|null => {
|
||||
if (c.value == null || c.value.length === 0) {
|
||||
return null;
|
||||
}
|
||||
const controlToCompare = c.root.get(controlNameToCompare);
|
||||
if (controlToCompare) {
|
||||
const subscription: Subscription = controlToCompare.valueChanges.subscribe(() => {
|
||||
c.updateValueAndValidity();
|
||||
subscription.unsubscribe();
|
||||
});
|
||||
}
|
||||
return controlToCompare && controlToCompare.value !== c.value ? {'notequal': true} : null;
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -20,8 +20,9 @@
|
||||
<h3>
|
||||
<i class="fa fa-stop" matTooltip="{{'labels.status.' + loanDetailsData.status.value | translate}}"
|
||||
[ngClass]="loanDetailsData.inArrears?'status-active-overdue':( iconLoanStatusColor() | statusLookup)"></i>
|
||||
<span class="m-r-10">{{'labels.heading.Loan Product' | translate}}:</span> {{loanDetailsData.loanProductName}}
|
||||
<mifosx-account-number accountNo="{{loanDetailsData.accountNo}}"></mifosx-account-number>
|
||||
<span class="m-r-5">{{'labels.heading.Loan Product' | translate}} :</span>
|
||||
<span class="m-r-5"><mifosx-long-text textValue="{{loanDetailsData.loanProductName}}"></mifosx-long-text></span>
|
||||
<mifosx-account-number accountNo="{{loanDetailsData.accountNo}}" display="left"></mifosx-account-number>
|
||||
</h3>
|
||||
<span class="loans-overview">
|
||||
<span class="m-r-10">{{'labels.text.' + entityType | translate}} {{"labels.inputs.name" | translate}} :</span> {{loanDetailsData.clientName ||
|
||||
|
||||
@@ -26,8 +26,17 @@
|
||||
</span>
|
||||
<mat-label>{{'labels.inputs.Confirm Password' | translate}}</mat-label>
|
||||
<input type="password" matInput required formControlName="repeatPassword">
|
||||
<mat-error *ngIf="resetPasswordForm.controls.repeatPassword.hasError('required')">
|
||||
{{'labels.inputs.Confirm Password' | translate}} <strong>{{'labels.commons.is required' | translate}}</strong>
|
||||
<mat-error *ngIf="resetPasswordForm.controls.password.hasError('required')">
|
||||
{{ 'labels.inputs.Password' | translate }} <strong>{{'labels.commons.is required' | translate }}</strong>
|
||||
</mat-error>
|
||||
<mat-error *ngIf="resetPasswordForm.controls.password.hasError('minlength')">
|
||||
{{'labels.commons.Password should be at least' | translate }} <strong>{{'labels.commons.8 characters long' | translate }}</strong>
|
||||
</mat-error>
|
||||
<mat-error *ngIf="resetPasswordForm.controls.password.hasError('maxlength')">
|
||||
{{'labels.commons.Password should not be more than' | translate }} <strong>{{'labels.commons.50 characters long' | translate }}</strong>
|
||||
</mat-error>
|
||||
<mat-error *ngIf="resetPasswordForm.controls.password.hasError('pattern') && !(resetPasswordForm.controls.password.hasError('minlength') || resetPasswordForm.controls.password.hasError('maxlength'))">
|
||||
{{'labels.commons.Password should include a' | translate }} <strong>{{'labels.commons.numeral' | translate }}</strong> {{'labels.commons.and at' | translate }} <strong>{{'labels.commons.least one uppercase' | translate }}</strong> ,<strong>{{'labels.commons.lowercase and special character' | translate }}</strong>
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ import { AuthenticationService } from '../../core/authentication/authentication.
|
||||
|
||||
/** Custom Validators */
|
||||
import { confirmPasswordValidator } from './confirm-password.validator';
|
||||
import { PasswordsUtility } from 'app/core/utils/passwords-utility';
|
||||
|
||||
/**
|
||||
* Reset password component.
|
||||
@@ -33,7 +34,8 @@ export class ResetPasswordComponent implements OnInit {
|
||||
* @param {AuthenticationService} authenticationService Authentication Service.
|
||||
*/
|
||||
constructor(private formBuilder: UntypedFormBuilder,
|
||||
private authenticationService: AuthenticationService) { }
|
||||
private authenticationService: AuthenticationService,
|
||||
private passwordsUtility: PasswordsUtility) { }
|
||||
|
||||
/**
|
||||
* Creates reset password form.
|
||||
@@ -66,7 +68,7 @@ export class ResetPasswordComponent implements OnInit {
|
||||
*/
|
||||
private createResetPasswordForm() {
|
||||
this.resetPasswordForm = this.formBuilder.group({
|
||||
'password': ['', Validators.required],
|
||||
'password': ['', this.passwordsUtility.getPasswordValidators()],
|
||||
'repeatPassword': ['', Validators.required]
|
||||
}, { validator: confirmPasswordValidator });
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ export class OfficesComponent implements OnInit, AfterViewInit {
|
||||
/** Office data tables. */
|
||||
dataTablesData: any;
|
||||
/** Flag to check if tree is expanded or collapsed. */
|
||||
isTreeExpanded: boolean = true;
|
||||
isTreeExpanded = true;
|
||||
|
||||
/** Paginator for offices table. */
|
||||
@ViewChild(MatPaginator, { static: true }) paginator: MatPaginator;
|
||||
@@ -190,9 +190,9 @@ export class OfficesComponent implements OnInit, AfterViewInit {
|
||||
this.router.navigate(['/organization']);
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* Expand and Collapse the tree
|
||||
*/
|
||||
*/
|
||||
toggleExpandCollapse() {
|
||||
this.isTreeExpanded = this.treeControlService.toggleExpandCollapse(this.nestedTreeControl, this.isTreeExpanded);
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ import { FormatNumberPipe } from './format-number.pipe';
|
||||
import { YesnoPipe } from './yesno.pipe';
|
||||
import { PrettyPrintPipe } from './pretty-print.pipe';
|
||||
import { TranslateCatalogPipe } from './translate-catalog.pipe';
|
||||
import { TruncateTextPipe } from './truncate-text.pipe';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
@@ -30,7 +31,8 @@ import { TranslateCatalogPipe } from './translate-catalog.pipe';
|
||||
FormatNumberPipe,
|
||||
YesnoPipe,
|
||||
PrettyPrintPipe,
|
||||
TranslateCatalogPipe],
|
||||
TranslateCatalogPipe,
|
||||
TruncateTextPipe],
|
||||
providers: [StatusLookupPipe,
|
||||
AccountsFilterPipe,
|
||||
ChargesFilterPipe,
|
||||
@@ -43,7 +45,8 @@ import { TranslateCatalogPipe } from './translate-catalog.pipe';
|
||||
FormatNumberPipe,
|
||||
YesnoPipe,
|
||||
PrettyPrintPipe,
|
||||
TranslateCatalogPipe],
|
||||
TranslateCatalogPipe,
|
||||
TruncateTextPipe],
|
||||
exports: [StatusLookupPipe,
|
||||
AccountsFilterPipe,
|
||||
ChargesFilterPipe,
|
||||
@@ -56,6 +59,7 @@ import { TranslateCatalogPipe } from './translate-catalog.pipe';
|
||||
FormatNumberPipe,
|
||||
YesnoPipe,
|
||||
PrettyPrintPipe,
|
||||
TranslateCatalogPipe]
|
||||
TranslateCatalogPipe,
|
||||
TruncateTextPipe]
|
||||
})
|
||||
export class PipesModule { }
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
import { Pipe, PipeTransform } from '@angular/core';
|
||||
|
||||
@Pipe({
|
||||
name: 'truncateText'
|
||||
})
|
||||
export class TruncateTextPipe implements PipeTransform {
|
||||
|
||||
transform(value: string, chars: number): string {
|
||||
if (value.length <= 40) {
|
||||
return value;
|
||||
}
|
||||
|
||||
let truncatedText = value.substring(0, 30);
|
||||
if (chars) {
|
||||
truncatedText = value.substring(0, chars);
|
||||
}
|
||||
|
||||
return truncatedText;
|
||||
}
|
||||
|
||||
}
|
||||
+2
-2
@@ -34,7 +34,7 @@
|
||||
<mat-label>{{'labels.inputs.Debit Account Type' | translate}}</mat-label>
|
||||
<mat-select formControlName="debitAccountType">
|
||||
<mat-option *ngFor="let debitAccountType of debitAccountTypeData" [value]="debitAccountType.id">
|
||||
{{ 'labels.inputs.accounting.' + debitAccountType.value | translate }}
|
||||
{{ debitAccountType.value | translateCatalog }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
@@ -48,7 +48,7 @@
|
||||
<mat-label>{{'labels.inputs.Credit Account Type' | translate}}</mat-label>
|
||||
<mat-select formControlName="creditAccountType">
|
||||
<mat-option *ngFor="let creditAccountType of creditAccountTypeData" [value]="creditAccountType.id">
|
||||
{{ 'labels.inputs.accounting.' + creditAccountType.value | translate }}
|
||||
{{ creditAccountType.value | translateCatalog }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
|
||||
+1
-1
@@ -50,7 +50,7 @@
|
||||
</div>
|
||||
|
||||
<div fxFlex="50%" *ngIf="taxComponentData.creditAccountType">
|
||||
{{ 'labels.inputs.accounting.' + taxComponentData.creditAccountType.value | translate }}
|
||||
{{ taxComponentData.creditAccountType.value | translateCatalog }}
|
||||
</div>
|
||||
|
||||
<div fxFlex="50%" class="mat-body-strong" *ngIf="taxComponentData.creditAccount">
|
||||
|
||||
@@ -19,8 +19,9 @@
|
||||
[matTooltip]="savingsAccountData.status.value" *ngIf="!savingsAccountData.subStatus.block"></i>
|
||||
<i class="fa fa-stop" [ngClass]="savingsAccountData.subStatus.value | statusLookup"
|
||||
[matTooltip]="savingsAccountData.subStatus.value" *ngIf="savingsAccountData.subStatus.block"></i>
|
||||
{{'labels.inputs.Savings Product' | translate}} : {{savingsAccountData.savingsProductName}}<span class="m-l-10">
|
||||
<mifosx-account-number accountNo="{{savingsAccountData.accountNo}}"></mifosx-account-number></span>
|
||||
<span class="m-r-5">{{'labels.inputs.Savings Product' | translate}} :</span>
|
||||
<span class="m-r-5"><mifosx-long-text textValue="{{savingsAccountData.savingsProductName}}"></mifosx-long-text></span>
|
||||
<mifosx-account-number accountNo="{{savingsAccountData.accountNo}}" display="left"></mifosx-account-number>
|
||||
</h3>
|
||||
<span class="account-overview">
|
||||
{{'labels.text.' + entityType | translate}} {{"labels.inputs.name" | translate}}: {{savingsAccountData.clientName ||
|
||||
|
||||
@@ -51,6 +51,9 @@ export class SearchPageComponent {
|
||||
case 'CLIENT':
|
||||
this.router.navigate(['clients', entity.entityId, 'general']);
|
||||
break;
|
||||
case 'CLIENTIDENTIFIER':
|
||||
this.router.navigate(['clients', entity.parentId, 'general']);
|
||||
break;
|
||||
case 'CENTER':
|
||||
this.router.navigate(['centers', entity.entityId]);
|
||||
break;
|
||||
|
||||
@@ -3,6 +3,7 @@ import { Component, OnInit, Inject } from '@angular/core';
|
||||
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
|
||||
import { UntypedFormBuilder, Validators, ValidatorFn, AbstractControl, ValidationErrors } from '@angular/forms';
|
||||
import { Subscription } from 'rxjs';
|
||||
import { PasswordsUtility } from 'app/core/utils/passwords-utility';
|
||||
|
||||
/**
|
||||
* Change Password Dialog component.
|
||||
@@ -23,7 +24,8 @@ export class ChangePasswordDialogComponent implements OnInit {
|
||||
*/
|
||||
constructor(public dialogRef: MatDialogRef<ChangePasswordDialogComponent>,
|
||||
@Inject(MAT_DIALOG_DATA) public data: any,
|
||||
private formBuilder: UntypedFormBuilder) { }
|
||||
private formBuilder: UntypedFormBuilder,
|
||||
private passwordsUtility: PasswordsUtility) { }
|
||||
|
||||
ngOnInit() {
|
||||
this.createChangePasswordForm();
|
||||
@@ -32,7 +34,7 @@ export class ChangePasswordDialogComponent implements OnInit {
|
||||
/** Change Password form */
|
||||
createChangePasswordForm() {
|
||||
this.changePasswordForm = this.formBuilder.group({
|
||||
'password': ['', [Validators.required, Validators.pattern('^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9])(?=.*[#$@$!%*?&])[A-Za-z\d#$@$!%*?&].{8,}$'), Validators.maxLength(50), Validators.minLength(8)]],
|
||||
'password': ['', this.passwordsUtility.getPasswordValidators()],
|
||||
'repeatPassword': ['', [Validators.required, this.confirmPassword('password')]]
|
||||
});
|
||||
}
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
<span (mouseenter)="mouseEnter()" (mouseleave)="mouseLeave()" *ngIf="!emptyValue">
|
||||
<span class="m-l-3" *ngIf="isLongValue()"><fa-icon icon="eye" size="sm" [title]="textValue"></fa-icon></span>
|
||||
<span class="m-l-5" *ngIf="isLongValue()">{{ textValue | truncateText:chars }}...</span>
|
||||
<span class="m-l-5" *ngIf="!isLongValue()">{{ textValue }}</span>
|
||||
</span>
|
||||
@@ -0,0 +1,23 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { LongTextComponent } from './long-text.component';
|
||||
|
||||
describe('LongTextComponent', () => {
|
||||
let component: LongTextComponent;
|
||||
let fixture: ComponentFixture<LongTextComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ LongTextComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(LongTextComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,37 @@
|
||||
import { Component, Input, OnInit } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'mifosx-long-text',
|
||||
templateUrl: './long-text.component.html',
|
||||
styleUrls: ['./long-text.component.scss']
|
||||
})
|
||||
export class LongTextComponent implements OnInit {
|
||||
@Input() textValue: string;
|
||||
@Input() chars = 30;
|
||||
|
||||
iconVisible = false;
|
||||
displayL = false;
|
||||
displayR = true;
|
||||
emptyValue = false;
|
||||
|
||||
constructor() { }
|
||||
|
||||
ngOnInit(): void {
|
||||
this.emptyValue = (!this.textValue || this.textValue === '');
|
||||
}
|
||||
|
||||
isLongValue(): boolean {
|
||||
if (this.textValue == null) {
|
||||
return false;
|
||||
}
|
||||
return (this.textValue.length > 25);
|
||||
}
|
||||
|
||||
mouseEnter() {
|
||||
this.iconVisible = true;
|
||||
}
|
||||
|
||||
mouseLeave() {
|
||||
this.iconVisible = false;
|
||||
}
|
||||
}
|
||||
@@ -47,6 +47,7 @@ import { GlAccountSelectorComponent } from './accounting/gl-account-selector/gl-
|
||||
import { GlAccountDisplayComponent } from './accounting/gl-account-display/gl-account-display.component';
|
||||
import { ViewSavingsAccountingDetailsComponent } from './accounting/view-savings-accounting-details/view-savings-accounting-details.component';
|
||||
import { ThemeToggleComponent } from './theme-toggle/theme-toggle.component';
|
||||
import { LongTextComponent } from './long-text/long-text.component';
|
||||
|
||||
/**
|
||||
* Shared Module
|
||||
@@ -100,7 +101,8 @@ import { ThemeToggleComponent } from './theme-toggle/theme-toggle.component';
|
||||
GlAccountSelectorComponent,
|
||||
GlAccountDisplayComponent,
|
||||
ViewSavingsAccountingDetailsComponent,
|
||||
ThemeToggleComponent
|
||||
ThemeToggleComponent,
|
||||
LongTextComponent
|
||||
],
|
||||
exports: [
|
||||
FileUploadComponent,
|
||||
@@ -132,7 +134,8 @@ import { ThemeToggleComponent } from './theme-toggle/theme-toggle.component';
|
||||
GlAccountSelectorComponent,
|
||||
GlAccountDisplayComponent,
|
||||
ViewSavingsAccountingDetailsComponent,
|
||||
ThemeToggleComponent
|
||||
ThemeToggleComponent,
|
||||
LongTextComponent
|
||||
]
|
||||
})
|
||||
export class SharedModule { }
|
||||
|
||||
@@ -67,6 +67,15 @@
|
||||
<mat-error *ngIf="userForm.controls.password.hasError('required')">
|
||||
{{'labels.inputs.Password' | translate}} {{"labels.commons.is" | translate}} <strong>{{"labels.commons.required" | translate}}</strong>
|
||||
</mat-error>
|
||||
<mat-error *ngIf="userForm.controls.password.hasError('minlength')">
|
||||
{{'labels.commons.Password should be at least' | translate }} <strong>{{'labels.commons.8 characters long' | translate }}</strong>
|
||||
</mat-error>
|
||||
<mat-error *ngIf="userForm.controls.password.hasError('maxlength')">
|
||||
{{'labels.commons.Password should not be more than' | translate }} <strong>{{'labels.commons.50 characters long' | translate }}</strong>
|
||||
</mat-error>
|
||||
<mat-error *ngIf="userForm.controls.password.hasError('pattern') && !(userForm.controls.password.hasError('minlength') || userForm.controls.password.hasError('maxlength'))">
|
||||
{{'labels.commons.Password should include a' | translate }} <strong>{{'labels.commons.numeral' | translate }}</strong> {{'labels.commons.and at' | translate }} <strong>{{'labels.commons.least one uppercase' | translate }}</strong> ,<strong>{{'labels.commons.lowercase and special character' | translate }}</strong>
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field fxFlex="48%" *ngIf="!userForm.controls.sendPasswordToEmail.value">
|
||||
@@ -79,7 +88,7 @@
|
||||
|
||||
<p fxFlex="98%" class="reset-password-error" *ngIf="userForm.errors?.passwordsDoNotMatch &&
|
||||
(!userForm.controls.password.hasError('required') && !userForm.controls.repeatPassword.hasError('required'))">
|
||||
{{'labels.inputs.Passwords' | translate}} <strong>{{"labels.commons.do not match' | translate}}</strong>
|
||||
{{'labels.inputs.Passwords' | translate}} <strong>{{'labels.commons.do not match' | translate}}</strong>
|
||||
</p>
|
||||
|
||||
<mat-form-field fxFlex="48%">
|
||||
|
||||
@@ -8,13 +8,12 @@ import { MatDialog } from '@angular/material/dialog';
|
||||
/** Custom Services */
|
||||
import { UsersService } from '../users.service';
|
||||
import { PopoverService } from '../../configuration-wizard/popover/popover.service';
|
||||
import { ConfigurationWizardService } from '../../configuration-wizard/configuration-wizard.service';
|
||||
|
||||
/** Custom Validators */
|
||||
import { confirmPasswordValidator } from '../../login/reset-password/confirm-password.validator';
|
||||
|
||||
/** Custom Dialog Component */
|
||||
import { ContinueSetupDialogComponent } from '../../configuration-wizard/continue-setup-dialog/continue-setup-dialog.component';
|
||||
import { PasswordsUtility } from 'app/core/utils/passwords-utility';
|
||||
import { confirmPasswordValidator } from 'app/login/reset-password/confirm-password.validator';
|
||||
import { ConfigurationWizardService } from 'app/configuration-wizard/configuration-wizard.service';
|
||||
import { ContinueSetupDialogComponent } from 'app/configuration-wizard/continue-setup-dialog/continue-setup-dialog.component';
|
||||
|
||||
/**
|
||||
* Create user component.
|
||||
@@ -55,7 +54,8 @@ export class CreateUserComponent implements OnInit, AfterViewInit {
|
||||
private router: Router,
|
||||
private popoverService: PopoverService,
|
||||
private configurationWizardService: ConfigurationWizardService,
|
||||
public dialog: MatDialog) {
|
||||
private dialog: MatDialog,
|
||||
private passwordsUtility: PasswordsUtility) {
|
||||
this.route.data.subscribe((data: {
|
||||
usersTemplate: any
|
||||
}) => {
|
||||
@@ -112,8 +112,8 @@ export class CreateUserComponent implements OnInit, AfterViewInit {
|
||||
this.userForm.removeControl('repeatPassword');
|
||||
this.userForm.get('email').setValidators([Validators.required, Validators.email]);
|
||||
} else {
|
||||
this.userForm.addControl('password', new UntypedFormControl('', Validators.required));
|
||||
this.userForm.addControl('repeatPassword', new UntypedFormControl('', Validators.required));
|
||||
this.userForm.addControl('password', new UntypedFormControl('', this.passwordsUtility.getPasswordValidators()));
|
||||
this.userForm.addControl('repeatPassword', new UntypedFormControl('', [Validators.required, this.passwordsUtility.confirmPassword('password')]));
|
||||
this.userForm.get('email').setValidators([Validators.email]);
|
||||
}
|
||||
this.userForm.get('email').updateValueAndValidity();
|
||||
|
||||
@@ -644,6 +644,8 @@
|
||||
"THURSDAY": "ČTVRTEK",
|
||||
"FRIDAY": "PÁTEK",
|
||||
"SATURDAY": "SOBOTA",
|
||||
"Interest posting": "Zveřejnění zájmu",
|
||||
"Amount on hold": "Částka na počkání",
|
||||
"Invalid": "Neplatný"
|
||||
},
|
||||
"commons": {
|
||||
|
||||
@@ -644,6 +644,8 @@
|
||||
"THURSDAY": "DONNERSTAG",
|
||||
"FRIDAY": "FREITAG",
|
||||
"SATURDAY": "SAMSTAG",
|
||||
"Interest posting": "Zinsbuchung",
|
||||
"Amount on hold": "Zurückgestellter Betrag",
|
||||
"Invalid": "Ungültig"
|
||||
},
|
||||
"commons": {
|
||||
|
||||
@@ -644,6 +644,8 @@
|
||||
"THURSDAY": "THURSDAY",
|
||||
"FRIDAY": "FRIDAY",
|
||||
"SATURDAY": "SATURDAY",
|
||||
"Interest posting": "Interest posting",
|
||||
"Amount on hold": "Amount on hold",
|
||||
"Invalid": "Invalid"
|
||||
},
|
||||
"commons": {
|
||||
|
||||
@@ -644,6 +644,8 @@
|
||||
"THURSDAY": "JUEVES",
|
||||
"FRIDAY": "VIERNES",
|
||||
"SATURDAY": "SÁBADO",
|
||||
"Interest posting": "Intereses",
|
||||
"Amount on hold": "Retención",
|
||||
"Invalid": "Inválido"
|
||||
},
|
||||
"commons": {
|
||||
|
||||
@@ -644,6 +644,8 @@
|
||||
"THURSDAY": "JEUDI",
|
||||
"FRIDAY": "VENDREDI",
|
||||
"SATURDAY": "SAMEDI",
|
||||
"Interest posting": "Publication d'intérêt",
|
||||
"Amount on hold": "Montant retenu",
|
||||
"Invalid": "Invalide"
|
||||
},
|
||||
"commons": {
|
||||
|
||||
@@ -644,6 +644,8 @@
|
||||
"THURSDAY": "GIOVEDÌ",
|
||||
"FRIDAY": "VENERDÌ",
|
||||
"SATURDAY": "SABATO",
|
||||
"Interest posting": "Pubblicazione di interessi",
|
||||
"Amount on hold": "Importo sospeso",
|
||||
"Invalid": "Non valido"
|
||||
},
|
||||
"commons": {
|
||||
|
||||
@@ -644,6 +644,8 @@
|
||||
"THURSDAY": "목요일",
|
||||
"FRIDAY": "금요일",
|
||||
"SATURDAY": "토요일",
|
||||
"Interest posting": "관심 게시",
|
||||
"Amount on hold": "보류 금액",
|
||||
"Invalid": "유효하지 않은"
|
||||
},
|
||||
"commons": {
|
||||
|
||||
@@ -644,6 +644,8 @@
|
||||
"THURSDAY": "KETVIRTADIENIS",
|
||||
"FRIDAY": "PENKTADIENIS",
|
||||
"SATURDAY": "ŠEŠTADIENIS",
|
||||
"Interest posting": "Interesų skelbimas",
|
||||
"Amount on hold": "Sulaikyta suma",
|
||||
"Invalid": "Neteisinga"
|
||||
},
|
||||
"commons": {
|
||||
|
||||
@@ -644,6 +644,8 @@
|
||||
"THURSDAY": "CETURTDIENA",
|
||||
"FRIDAY": "PIEKTDIENA",
|
||||
"SATURDAY": "SESTDIENA",
|
||||
"Interest posting": "Interešu publicēšana",
|
||||
"Amount on hold": "Aizturētā summa",
|
||||
"Invalid": "Nederīgs"
|
||||
},
|
||||
"commons": {
|
||||
|
||||
@@ -644,6 +644,8 @@
|
||||
"THURSDAY": "बिहीबार",
|
||||
"FRIDAY": "शुक्रबार",
|
||||
"SATURDAY": "शनिबार",
|
||||
"Interest posting": "चासो पोस्टिंग",
|
||||
"Amount on hold": "होल्डमा रहेको रकम",
|
||||
"Invalid": "अवैध"
|
||||
},
|
||||
"commons": {
|
||||
|
||||
@@ -644,6 +644,8 @@
|
||||
"THURSDAY": "QUINTA-FEIRA",
|
||||
"FRIDAY": "SEXTA-FEIRA",
|
||||
"SATURDAY": "SÁBADO",
|
||||
"Interest posting": "Postagem de interesse",
|
||||
"Amount on hold": "Valor retido",
|
||||
"Invalid": "Inválido"
|
||||
},
|
||||
"commons": {
|
||||
|
||||
@@ -644,6 +644,8 @@
|
||||
"THURSDAY": "ALHAMISI",
|
||||
"FRIDAY": "IJUMAA",
|
||||
"SATURDAY": "JUMAMOSI",
|
||||
"Interest posting": "Nia ya kuchapisha",
|
||||
"Amount on hold": "Kiasi kilichohifadhiwa",
|
||||
"Invalid": "Batili"
|
||||
},
|
||||
"commons": {
|
||||
|
||||
Reference in New Issue
Block a user