mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-07 10:04:11 +00:00
69432: Functional profile security form + notifications + metadata form refactoring
This commit is contained in:
@@ -1441,12 +1441,26 @@
|
|||||||
|
|
||||||
"profile.metadata.form.notifications.success.title": "Profile saved",
|
"profile.metadata.form.notifications.success.title": "Profile saved",
|
||||||
|
|
||||||
|
"profile.notifications.warning.no-changes.content": "No changes were made to the Profile.",
|
||||||
|
|
||||||
|
"profile.notifications.warning.no-changes.title": "No changes",
|
||||||
|
|
||||||
|
"profile.security.form.error.matching-passwords": "The passwords do not match.",
|
||||||
|
|
||||||
"profile.security.form.info": "Optionally, you can enter a new password in the box below, and confirm it by typing it again into the second box. It should be at least six characters long.",
|
"profile.security.form.info": "Optionally, you can enter a new password in the box below, and confirm it by typing it again into the second box. It should be at least six characters long.",
|
||||||
|
|
||||||
"profile.security.form.label.password": "Password",
|
"profile.security.form.label.password": "Password",
|
||||||
|
|
||||||
"profile.security.form.label.passwordrepeat": "Retype to confirm",
|
"profile.security.form.label.passwordrepeat": "Retype to confirm",
|
||||||
|
|
||||||
|
"profile.security.form.notifications.success.content": "Successfully changed password.",
|
||||||
|
|
||||||
|
"profile.security.form.notifications.success.title": "Password changed",
|
||||||
|
|
||||||
|
"profile.security.form.notifications.error.title": "Error changing passwords",
|
||||||
|
|
||||||
|
"profile.security.form.notifications.error.not-same": "The provided passwords are not the same.",
|
||||||
|
|
||||||
"profile.title": "Update Profile",
|
"profile.title": "Update Profile",
|
||||||
|
|
||||||
|
|
||||||
|
@@ -20,7 +20,7 @@ import { EPERSON } from './models/eperson.resource-type';
|
|||||||
@dataService(EPERSON)
|
@dataService(EPERSON)
|
||||||
export class EPersonDataService extends DataService<EPerson> {
|
export class EPersonDataService extends DataService<EPerson> {
|
||||||
|
|
||||||
protected linkPath: 'epersons';
|
protected linkPath = 'epersons';
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
protected requestService: RequestService,
|
protected requestService: RequestService,
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
<ds-form *ngIf="formModel"
|
<ds-form *ngIf="formModel"
|
||||||
[formId]="'profile-page-metadata-form-id'"
|
[formId]="'profile-page-metadata-form-id'"
|
||||||
[formModel]="formModel"
|
[formModel]="formModel"
|
||||||
|
[formGroup]="formGroup"
|
||||||
[displaySubmit]="false">
|
[displaySubmit]="false">
|
||||||
</ds-form>
|
</ds-form>
|
||||||
|
@@ -29,12 +29,6 @@ export class ProfilePageMetadataFormComponent implements OnInit {
|
|||||||
*/
|
*/
|
||||||
@Input() user: EPerson;
|
@Input() user: EPerson;
|
||||||
|
|
||||||
/**
|
|
||||||
* Reference to the form component
|
|
||||||
* Used for validating the form before sending update requests
|
|
||||||
*/
|
|
||||||
@ViewChild(FormComponent, { static: false }) formRef: FormComponent;
|
|
||||||
|
|
||||||
formModel: DynamicFormControlModel[] = [
|
formModel: DynamicFormControlModel[] = [
|
||||||
new DynamicInputModel({
|
new DynamicInputModel({
|
||||||
id: 'email',
|
id: 'email',
|
||||||
@@ -91,7 +85,6 @@ export class ProfilePageMetadataFormComponent implements OnInit {
|
|||||||
|
|
||||||
constructor(@Inject(GLOBAL_CONFIG) protected config: GlobalConfig,
|
constructor(@Inject(GLOBAL_CONFIG) protected config: GlobalConfig,
|
||||||
protected location: Location,
|
protected location: Location,
|
||||||
protected formService: FormService,
|
|
||||||
protected formBuilderService: FormBuilderService,
|
protected formBuilderService: FormBuilderService,
|
||||||
protected translate: TranslateService,
|
protected translate: TranslateService,
|
||||||
protected epersonService: EPersonDataService,
|
protected epersonService: EPersonDataService,
|
||||||
@@ -139,37 +132,47 @@ export class ProfilePageMetadataFormComponent implements OnInit {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
updateProfile() {
|
updateProfile(): boolean {
|
||||||
if (!this.formRef.formGroup.valid) {
|
if (!this.formGroup.valid) {
|
||||||
this.formService.validateAllFormFields(this.formGroup);
|
return false;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const newMetadata = cloneDeep(this.user.metadata);
|
const newMetadata = cloneDeep(this.user.metadata);
|
||||||
|
let changed = false;
|
||||||
this.formModel.filter((fieldModel) => fieldModel.id !== 'email').forEach((fieldModel: DynamicFormValueControlModel<string>) => {
|
this.formModel.filter((fieldModel) => fieldModel.id !== 'email').forEach((fieldModel: DynamicFormValueControlModel<string>) => {
|
||||||
if (newMetadata.hasOwnProperty(fieldModel.name) && newMetadata[fieldModel.name].length > 0) {
|
if (newMetadata.hasOwnProperty(fieldModel.name) && newMetadata[fieldModel.name].length > 0) {
|
||||||
if (hasValue(fieldModel.value)) {
|
if (hasValue(fieldModel.value)) {
|
||||||
newMetadata[fieldModel.name][0].value = fieldModel.value;
|
if (newMetadata[fieldModel.name][0].value !== fieldModel.value) {
|
||||||
|
newMetadata[fieldModel.name][0].value = fieldModel.value;
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
newMetadata[fieldModel.name] = [];
|
newMetadata[fieldModel.name] = [];
|
||||||
|
changed = true;
|
||||||
}
|
}
|
||||||
} else if (hasValue(fieldModel.value)) {
|
} else if (hasValue(fieldModel.value)) {
|
||||||
newMetadata[fieldModel.name] = [{
|
newMetadata[fieldModel.name] = [{
|
||||||
value: fieldModel.value,
|
value: fieldModel.value,
|
||||||
language: null
|
language: null
|
||||||
} as any];
|
} as any];
|
||||||
|
changed = true;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
this.epersonService.update(Object.assign(cloneDeep(this.user), { metadata: newMetadata })).pipe(
|
|
||||||
getSucceededRemoteData(),
|
if (changed) {
|
||||||
getRemoteDataPayload()
|
this.epersonService.update(Object.assign(cloneDeep(this.user), {metadata: newMetadata})).pipe(
|
||||||
).subscribe((user) => {
|
getSucceededRemoteData(),
|
||||||
this.user = user;
|
getRemoteDataPayload()
|
||||||
this.setFormValues();
|
).subscribe((user) => {
|
||||||
this.notificationsService.success(
|
this.user = user;
|
||||||
this.translate.instant(this.NOTIFICATION_PREFIX + 'success.title'),
|
this.setFormValues();
|
||||||
this.translate.instant(this.NOTIFICATION_PREFIX + 'success.content')
|
this.notificationsService.success(
|
||||||
);
|
this.translate.instant(this.NOTIFICATION_PREFIX + 'success.title'),
|
||||||
});
|
this.translate.instant(this.NOTIFICATION_PREFIX + 'success.content')
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return changed;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -2,5 +2,7 @@
|
|||||||
<ds-form *ngIf="formModel"
|
<ds-form *ngIf="formModel"
|
||||||
[formId]="'profile-page-security-form-id'"
|
[formId]="'profile-page-security-form-id'"
|
||||||
[formModel]="formModel"
|
[formModel]="formModel"
|
||||||
|
[formGroup]="formGroup"
|
||||||
[displaySubmit]="false">
|
[displaySubmit]="false">
|
||||||
</ds-form>
|
</ds-form>
|
||||||
|
<div class="container-fluid text-danger" *ngIf="formGroup.hasError('notSame')">{{'profile.security.form.error.matching-passwords' | translate}}</div>
|
||||||
|
@@ -1,17 +1,26 @@
|
|||||||
import { Component, OnInit } from '@angular/core';
|
import { Component, Input, OnInit } from '@angular/core';
|
||||||
import {
|
import {
|
||||||
DynamicFormControlModel,
|
DynamicFormControlModel,
|
||||||
DynamicFormService,
|
DynamicFormService, DynamicFormValueControlModel,
|
||||||
DynamicInputModel
|
DynamicInputModel
|
||||||
} from '@ng-dynamic-forms/core';
|
} from '@ng-dynamic-forms/core';
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
import { FormGroup } from '@angular/forms';
|
import { FormGroup } from '@angular/forms';
|
||||||
|
import { hasValue, isEmpty, isNotEmpty } from '../../shared/empty.util';
|
||||||
|
import { EPersonDataService } from '../../core/eperson/eperson-data.service';
|
||||||
|
import { EPerson } from '../../core/eperson/models/eperson.model';
|
||||||
|
import { ErrorResponse, RestResponse } from '../../core/cache/response.models';
|
||||||
|
import { NotificationsService } from '../../shared/notifications/notifications.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-profile-page-security-form',
|
selector: 'ds-profile-page-security-form',
|
||||||
templateUrl: './profile-page-security-form.component.html'
|
templateUrl: './profile-page-security-form.component.html'
|
||||||
})
|
})
|
||||||
export class ProfilePageSecurityFormComponent implements OnInit {
|
export class ProfilePageSecurityFormComponent implements OnInit {
|
||||||
|
/**
|
||||||
|
* The user to display the form for
|
||||||
|
*/
|
||||||
|
@Input() user: EPerson;
|
||||||
|
|
||||||
formModel: DynamicFormControlModel[] = [
|
formModel: DynamicFormControlModel[] = [
|
||||||
new DynamicInputModel({
|
new DynamicInputModel({
|
||||||
@@ -31,14 +40,18 @@ export class ProfilePageSecurityFormComponent implements OnInit {
|
|||||||
*/
|
*/
|
||||||
formGroup: FormGroup;
|
formGroup: FormGroup;
|
||||||
|
|
||||||
|
NOTIFICATIONS_PREFIX = 'profile.security.form.notifications.';
|
||||||
|
|
||||||
LABEL_PREFIX = 'profile.security.form.label.';
|
LABEL_PREFIX = 'profile.security.form.label.';
|
||||||
|
|
||||||
constructor(protected formService: DynamicFormService,
|
constructor(protected formService: DynamicFormService,
|
||||||
protected translate: TranslateService) {
|
protected translate: TranslateService,
|
||||||
|
protected epersonService: EPersonDataService,
|
||||||
|
protected notificationsService: NotificationsService) {
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.formGroup = this.formService.createFormGroup(this.formModel);
|
this.formGroup = this.formService.createFormGroup(this.formModel, { validators: this.checkPasswords });
|
||||||
this.updateFieldTranslations();
|
this.updateFieldTranslations();
|
||||||
this.translate.onLangChange
|
this.translate.onLangChange
|
||||||
.subscribe(() => {
|
.subscribe(() => {
|
||||||
@@ -53,4 +66,40 @@ export class ProfilePageSecurityFormComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
checkPasswords(group: FormGroup) {
|
||||||
|
const pass = group.get('password').value;
|
||||||
|
const repeatPass = group.get('passwordrepeat').value;
|
||||||
|
|
||||||
|
return isEmpty(repeatPass) || pass === repeatPass ? null : { notSame: true };
|
||||||
|
}
|
||||||
|
|
||||||
|
updateSecurity() {
|
||||||
|
const pass = this.formGroup.get('password').value;
|
||||||
|
const passEntered = isNotEmpty(pass);
|
||||||
|
if (!this.formGroup.valid) {
|
||||||
|
if (passEntered) {
|
||||||
|
this.notificationsService.error(this.translate.instant(this.NOTIFICATIONS_PREFIX + 'error.not-same'));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (passEntered) {
|
||||||
|
const operation = Object.assign({ op: 'replace', path: '/password', value: pass });
|
||||||
|
this.epersonService.immediatePatch(this.user, [operation]).subscribe((response: RestResponse) => {
|
||||||
|
if (response.isSuccessful) {
|
||||||
|
this.notificationsService.success(
|
||||||
|
this.translate.instant(this.NOTIFICATIONS_PREFIX + 'success.title'),
|
||||||
|
this.translate.instant(this.NOTIFICATIONS_PREFIX + 'success.content')
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
this.notificationsService.error(
|
||||||
|
this.translate.instant(this.NOTIFICATIONS_PREFIX + 'error.title'), (response as ErrorResponse).errorMessage
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return passEntered;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -10,7 +10,7 @@
|
|||||||
<div class="card mb-4">
|
<div class="card mb-4">
|
||||||
<div class="card-header">{{'profile.card.security' | translate}}</div>
|
<div class="card-header">{{'profile.card.security' | translate}}</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<ds-profile-page-security-form></ds-profile-page-security-form>
|
<ds-profile-page-security-form [user]="user"></ds-profile-page-security-form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button class="btn btn-outline-primary" (click)="updateProfile()">{{'profile.form.submit' | translate}}</button>
|
<button class="btn btn-outline-primary" (click)="updateProfile()">{{'profile.form.submit' | translate}}</button>
|
||||||
|
@@ -5,6 +5,9 @@ import { select, Store } from '@ngrx/store';
|
|||||||
import { getAuthenticatedUser } from '../core/auth/selectors';
|
import { getAuthenticatedUser } from '../core/auth/selectors';
|
||||||
import { AppState } from '../app.reducer';
|
import { AppState } from '../app.reducer';
|
||||||
import { ProfilePageMetadataFormComponent } from './profile-page-metadata-form/profile-page-metadata-form.component';
|
import { ProfilePageMetadataFormComponent } from './profile-page-metadata-form/profile-page-metadata-form.component';
|
||||||
|
import { ProfilePageSecurityFormComponent } from './profile-page-security-form/profile-page-security-form.component';
|
||||||
|
import { NotificationsService } from '../shared/notifications/notifications.service';
|
||||||
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-profile-page',
|
selector: 'ds-profile-page',
|
||||||
@@ -17,12 +20,18 @@ export class ProfilePageComponent implements OnInit {
|
|||||||
|
|
||||||
@ViewChild(ProfilePageMetadataFormComponent, { static: false }) metadataForm: ProfilePageMetadataFormComponent;
|
@ViewChild(ProfilePageMetadataFormComponent, { static: false }) metadataForm: ProfilePageMetadataFormComponent;
|
||||||
|
|
||||||
|
@ViewChild(ProfilePageSecurityFormComponent, { static: false }) securityForm: ProfilePageSecurityFormComponent;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The authenticated user
|
* The authenticated user
|
||||||
*/
|
*/
|
||||||
user$: Observable<EPerson>;
|
user$: Observable<EPerson>;
|
||||||
|
|
||||||
constructor(private store: Store<AppState>) {
|
NOTIFICATIONS_PREFIX = 'profile.notifications.';
|
||||||
|
|
||||||
|
constructor(private store: Store<AppState>,
|
||||||
|
private notificationsService: NotificationsService,
|
||||||
|
private translate: TranslateService) {
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
@@ -30,6 +39,13 @@ export class ProfilePageComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
updateProfile() {
|
updateProfile() {
|
||||||
this.metadataForm.updateProfile();
|
const metadataChanged = this.metadataForm.updateProfile();
|
||||||
|
const securityChanged = this.securityForm.updateSecurity();
|
||||||
|
if (!metadataChanged && !securityChanged) {
|
||||||
|
this.notificationsService.warning(
|
||||||
|
this.translate.instant(this.NOTIFICATIONS_PREFIX + 'warning.no-changes.title'),
|
||||||
|
this.translate.instant(this.NOTIFICATIONS_PREFIX + 'warning.no-changes.content')
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user