Skip to content

Commit

Permalink
Drop Down List on Profile (fixes #1332) (#1335)
Browse files Browse the repository at this point in the history
  • Loading branch information
ambitdev01 authored and paulbert committed Jul 3, 2018
1 parent 39f9421 commit 7d4f958
Show file tree
Hide file tree
Showing 7 changed files with 165 additions and 140 deletions.
7 changes: 6 additions & 1 deletion src/app/home/home.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,15 @@ <h1 i18n>Planet Learning</h1>
</button>
<button mat-icon-button *ngIf="notifications.length === 0" routerLink="/notifications" i18n-title title="No Notification"><mat-icon>notifications_none</mat-icon></button>
<planet-language></planet-language>
<button mat-icon-button routerLink="/users/profile/{{user.name}}">
<button mat-icon-button [matMenuTriggerFor]="userProfile">
<img *ngIf="user._attachments; else accountIcon" class="profile-image-large" [src]="userImgSrc">
<ng-template #accountIcon><mat-icon>account_circle</mat-icon></ng-template>
</button>
<mat-menu #userProfile="matMenu" [overlapTrigger]="false">
<button mat-menu-item routerLink="/users/profile/{{user.name}}" i18n>View Profile</button>
<button mat-menu-item i18n routerLink="/users/update/{{user.name}}">Edit Profile</button>
<button mat-menu-item i18n planetChangePassword>Change Password</button>
</mat-menu>
</mat-toolbar>
<!--Notification dropdown menu-->
<mat-menu #notificationMenu="matMenu" [overlapTrigger]="false">
Expand Down
151 changes: 151 additions & 0 deletions src/app/shared/dialogs/change-password.directive.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
import { Directive, HostListener, Input } from '@angular/core';
import { forkJoin, of } from 'rxjs';
import { switchMap, catchError } from 'rxjs/operators';
import { UserService } from '../../shared/user.service';
import { CouchService } from '../../shared/couchdb.service';
import { Validators } from '@angular/forms';
import { DialogsFormService } from '../../shared/dialogs/dialogs-form.service';
import { PlanetMessageService } from '../../shared/planet-message.service';
import { debug } from '../../debug-operator';
import { CustomValidators } from '../../validators/custom-validators';
import { ValidatorService } from '../../validators/validator.service';

const changePasswordFields = [
{
'label': 'Old Password',
'type': 'textbox',
'inputType': 'password',
'name': 'oldPassword',
'placeholder': 'Old Password',
'required': true
},
{
'label': 'Password',
'type': 'textbox',
'inputType': 'password',
'name': 'password',
'placeholder': 'Password',
'required': true
},
{
'label': 'Confirm Password',
'type': 'textbox',
'inputType': 'password',
'name': 'confirmPassword',
'placeholder': 'Confirm Password',
'required': true
}
];

@Directive({
selector: '[planetChangePassword]'
})
export class ChangePasswordDirective {

@Input('planetChangePassword') userDetail: any;
dbName = '_users';
changePasswordFormGroup = {
oldPassword: [ '', Validators.required, ac => this.validatorService.checkOldPassword$(ac) ],
password: [
'',
Validators.compose([
Validators.required,
CustomValidators.matchPassword('confirmPassword', false)
])
],
confirmPassword: [
'',
Validators.compose([
Validators.required,
CustomValidators.matchPassword('password', true)
])
]
};

constructor(
private userService: UserService,
private couchService: CouchService,
private dialogsFormService: DialogsFormService,
private planetMessageService: PlanetMessageService,
private validatorService: ValidatorService
) {}

@HostListener('click')
openChangePasswordForm() {
const title = 'Change Password';
this.dialogsFormService
.confirm(title, changePasswordFields, this.changePasswordFormGroup)
.pipe(debug('Dialog confirm'))
.subscribe((res) => {
if (res !== undefined) {
this.changePassword(res, this.userDetail || this.userService.get());
}
});
}

changePassword(credentialData, userDetail) {
const updateDoc = Object.assign({ password: credentialData.password }, userDetail);
this.changePasswordRequest(updateDoc).pipe(switchMap((responses) => {
return forkJoin([ ...responses.map(r => of(r)), this.reinitSession(userDetail.name, credentialData.password) ]);
})).subscribe((responses) => {
const errors = responses.filter(r => r.ok === false);
if (errors.length === 0) {
this.planetMessageService.showMessage('Password successfully updated');
} else {
this.planetMessageService.showAlert(errors.map(e => e.reason).join(' & '));
}
}, (error) => this.planetMessageService.showAlert('Error changing password'));
}

changePasswordRequest(userData) {
// Manager role also has isUserAdmin true so check role to be empty
const isUserAdmin = (this.userService.get().isUserAdmin && !this.userService.get().roles.length);
return this.couchService.put(this.dbName + '/' + userData._id, userData).pipe(switchMap((res) => {
if (isUserAdmin) {
return forkJoin([ of(res), this.updateAdminPassword(userData), this.updatePasswordOnParent(userData) ]);
}
return of(res);
}));
}

passwordError(reason: string) {
return () => {
return of({ ok: false, reason: reason });
};
}

reinitSession(username, password) {
return forkJoin([
this.couchService.post('_session', { 'name': username, 'password': password }, { withCredentials: true }),
this.couchService.post('_session', { 'name': this.userService.getConfig().adminName, 'password': password },
{ withCredentials: true, domain: this.userService.getConfig().parentDomain })
]).pipe(catchError(() => {
// Silent error for now so other specific messages are shown
return of({ ok: true });
}));
}

updatePasswordOnParent(userData) {
const adminName = 'org.couchdb.user:' + this.userService.getConfig().adminName;
return this.couchService.get('_users/' + adminName , { domain: this.userService.getConfig().parentDomain })
.pipe(catchError(this.passwordError('Error changing password in parent planet')),
switchMap((data) => {
if (data.ok === false) {
return of(data);
}
const { derived_key, iterations, password_scheme, salt, ...profile } = data;
profile.password = userData.password;
return this.couchService.put(this.dbName + '/' + profile._id, profile,
{ domain: this.userService.getConfig().parentDomain });
}));
}

updateAdminPassword(userData) {
return this.couchService.put('_node/nonode@nohost/_config/admins/' + userData.name, userData.password)
.pipe(catchError(this.passwordError('Error changing admin password')),
switchMap((response) => {
return of(response);
}));
}

}
5 changes: 3 additions & 2 deletions src/app/shared/dialogs/planet-dialogs.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,17 @@ import { PlanetFormsModule } from '../planet-forms.module';
import { FeedbackDirective } from '../../feedback/feedback.directive';
import { DialogsListComponent } from './dialogs-list.component';
import { DialogsListService } from './dialogs-list.service';
import { ChangePasswordDirective } from './change-password.directive';

@NgModule({
imports: [
CommonModule, MaterialModule, FormsModule, ReactiveFormsModule, PlanetFormsModule
],
exports: [
DialogsFormComponent, DialogsViewComponent, DialogsPromptComponent, FeedbackDirective, DialogsListComponent
DialogsFormComponent, DialogsViewComponent, DialogsPromptComponent, FeedbackDirective, DialogsListComponent, ChangePasswordDirective
],
declarations: [
DialogsFormComponent, DialogsViewComponent, DialogsPromptComponent, FeedbackDirective, DialogsListComponent
DialogsFormComponent, DialogsViewComponent, DialogsPromptComponent, FeedbackDirective, DialogsListComponent, ChangePasswordDirective
],
providers: [
DialogsFormService, DialogsListService
Expand Down
2 changes: 1 addition & 1 deletion src/app/users/users-profile/users-profile.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
<mat-icon class="margin-lr-5" *ngIf="userDetail.gender" svgIcon="{{userDetail.gender}}"></mat-icon>
<span class="toolbar-fill"></span>
<button mat-button class="margin-lr-3" *ngIf="user.name === urlName || user.isUserAdmin" i18n [routerLink]="['/users/update/', urlName]"><mat-icon>mode_edit</mat-icon><span>Edit Profile</span></button>
<button mat-button class="margin-lr-3" *ngIf="user.name === urlName" i18n (click)="changePasswordForm(userDetail)"><span>Change password</span></button>
<a mat-button class="margin-lr-3" *ngIf="user.name === urlName" i18n [planetChangePassword]="userDetail">Change password</a>
</mat-toolbar>
<div class="view-container">
<div class="profile-container">
Expand Down
137 changes: 2 additions & 135 deletions src/app/users/users-profile/users-profile.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,8 @@ import { ActivatedRoute, ParamMap, Router } from '@angular/router';
import { CouchService } from '../../shared/couchdb.service';
import { environment } from '../../../environments/environment';
import { UserService } from '../../shared/user.service';
import { Validators } from '@angular/forms';
import { DialogsFormService } from '../../shared/dialogs/dialogs-form.service';
import { CustomValidators } from '../../validators/custom-validators';
import { forkJoin, of } from 'rxjs';
import { switchMap, catchError } from 'rxjs/operators';
import { PlanetMessageService } from '../../shared/planet-message.service';
import { ValidatorService } from '../../validators/validator.service';
import { debug } from '../../debug-operator';
import { of } from 'rxjs';
import { switchMap } from 'rxjs/operators';

@Component({
templateUrl: './users-profile.component.html',
Expand Down Expand Up @@ -38,9 +32,6 @@ export class UsersProfileComponent implements OnInit {
private couchService: CouchService,
private route: ActivatedRoute,
private userService: UserService,
private dialogsFormService: DialogsFormService,
private planetMessageService: PlanetMessageService,
private validatorService: ValidatorService,
private router: Router
) { }

Expand Down Expand Up @@ -70,130 +61,6 @@ export class UsersProfileComponent implements OnInit {
});
}

onSubmit(credentialData, userDetail) {
const updateDoc = Object.assign({ password: credentialData.password }, userDetail);
this.changePasswordRequest(updateDoc).pipe(switchMap((responses) => {
return forkJoin([ ...responses.map(r => of(r)), this.reinitSession(userDetail.name, credentialData.password) ]);
})).subscribe((responses) => {
const errors = responses.filter(r => !r.ok);
if (errors.length === 0) {
this.planetMessageService.showMessage('Password successfully updated');
} else {
this.planetMessageService.showAlert(errors.map(e => e.reason).join(' & '));
}
}, (error) => this.planetMessageService.showAlert('Error changing password'));
}

changePasswordRequest(userData) {
// Manager role also has isUserAdmin true so check role to be empty
const isUserAdmin = (this.userService.get().isUserAdmin && !this.userService.get().roles.length);
const observables = [ this.couchService.put(this.dbName + '/' + userData._id, userData) ];
if (isUserAdmin) {
// Update user in parent planet
observables.push(this.couchService.get('_users/' + userData._id , { domain: this.userService.getConfig().parentDomain })
.pipe(catchError(this.passwordError('Error changing password in parent planet')),
switchMap((data) => {
if (data.ok === false) {
return of(data);
}
const { derived_key, iterations, password_scheme, salt, ...profile } = data;
profile.password = userData.password;
return this.couchService.put(this.dbName + '/' + profile._id, profile,
{ domain: this.userService.getConfig().parentDomain });
}))
);
// Add response ok if there is not error on changing admin password
observables.push(
this.couchService.put('_node/nonode@nohost/_config/admins/' + userData.name, userData.password)
.pipe(catchError(this.passwordError('Error changing admin password')),
switchMap((response) => {
return of(response);
}))
);
}
return forkJoin(observables);
}

passwordError(reason: string) {
return () => {
return of({ ok: false, reason: reason });
};
}

reinitSession(username, password) {
return forkJoin([
this.couchService.post('_session', { 'name': username, 'password': password }, { withCredentials: true }),
this.couchService.post('_session', { 'name': username, 'password': password },
{ withCredentials: true, domain: this.userService.getConfig().parentDomain })
]).pipe(catchError(() => {
// Silent error for now so other specific messages are shown
return of({ ok: true });
}));
}

changePasswordForm(userDetail) {
const title = 'Change Password';
const fields = this.newChangePasswordFormFields();
const formGroup = this.newChangePasswordFormGroup();
this.dialogsFormService
.confirm(title, fields, formGroup)
.pipe(debug('Dialog confirm'))
.subscribe((res) => {
if (res !== undefined) {
this.onSubmit(res, userDetail);
}
});
}

newChangePasswordFormFields() {
return [
{
'label': 'Old Password',
'type': 'textbox',
'inputType': 'password',
'name': 'oldPassword',
'placeholder': 'Old Password',
'required': true
},
{
'label': 'Password',
'type': 'textbox',
'inputType': 'password',
'name': 'password',
'placeholder': 'Password',
'required': true
},
{
'label': 'Confirm Password',
'type': 'textbox',
'inputType': 'password',
'name': 'confirmPassword',
'placeholder': 'Confirm Password',
'required': true
}
];
}

newChangePasswordFormGroup() {
return {
oldPassword: [ '', Validators.required, ac => this.validatorService.checkOldPassword$(ac) ],
password: [
'',
Validators.compose([
Validators.required,
CustomValidators.matchPassword('confirmPassword', false)
])
],
confirmPassword: [
'',
Validators.compose([
Validators.required,
CustomValidators.matchPassword('password', true)
])
]
};
}

goBack() {
const currentUser = this.userService.get();
if (currentUser.isUserAdmin) {
Expand Down
2 changes: 2 additions & 0 deletions src/app/users/users.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { UsersUpdateComponent } from './users-update/users-update.component';
import { UsersRouterModule } from './users-router.module';
import { PlanetFormsModule } from '../shared/planet-forms.module';
import { MaterialModule } from '../shared/material.module';
import { PlanetDialogsModule } from '../shared/dialogs/planet-dialogs.module';

@NgModule({
imports: [
Expand All @@ -18,6 +19,7 @@ import { MaterialModule } from '../shared/material.module';
FormsModule,
ReactiveFormsModule,
PlanetFormsModule,
PlanetDialogsModule,
MaterialModule
],
declarations: [
Expand Down
1 change: 0 additions & 1 deletion tslint.json
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,6 @@
"use-input-property-decorator": true,
"use-output-property-decorator": true,
"use-host-property-decorator": true,
"no-input-rename": true,
"no-output-rename": true,
"use-life-cycle-interface": true,
"use-pipe-transform-interface": true,
Expand Down

0 comments on commit 7d4f958

Please sign in to comment.