Skip to content

Commit

Permalink
Merged dspace-cris-2023_02_x into task/dspace-cris-2023_02_x/DSC-1841
Browse files Browse the repository at this point in the history
  • Loading branch information
Andrea Barbasso committed Jul 29, 2024
2 parents a64aefe + f37dc08 commit 0c1a726
Show file tree
Hide file tree
Showing 24 changed files with 286 additions and 51 deletions.
6 changes: 5 additions & 1 deletion src/app/app-routing.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ import {
import { ServerCheckGuard } from './core/server-check/server-check.guard';
import { MenuResolver } from './menu.resolver';
import { ThemedPageErrorComponent } from './page-error/themed-page-error.component';
import { ForgotPasswordCheckGuard } from './core/rest-property/forgot-password-check-guard.guard';
import { SUGGESTION_MODULE_PATH } from './suggestions-page/suggestions-page-routing-paths';
import { RedirectService } from './redirect/redirect.service';

Expand Down Expand Up @@ -99,7 +100,10 @@ import { RedirectService } from './redirect/redirect.service';
path: FORGOT_PASSWORD_PATH,
loadChildren: () => import('./forgot-password/forgot-password.module')
.then((m) => m.ForgotPasswordModule),
canActivate: [EndUserAgreementCurrentUserGuard]
canActivate: [
ForgotPasswordCheckGuard,
EndUserAgreementCurrentUserGuard
]
},
{
path: COMMUNITY_MODULE_PATH,
Expand Down
1 change: 1 addition & 0 deletions src/app/core/data/feature-authorization/feature-id.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ export enum FeatureID {
CanEditItem = 'canEditItem',
CanRegisterDOI = 'canRegisterDOI',
CanSubscribe = 'canSubscribeDso',
EPersonForgotPassword = 'epersonForgotPassword',
ShowClaimItem = 'showClaimItem',
CanCorrectItem = 'canCorrectItem',
}
1 change: 1 addition & 0 deletions src/app/core/locale/locale.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ export class LocaleService {
this.routeService.getQueryParameterValue('lang').subscribe(lang => {
if (lang && this.translate.getLangs().includes(lang)) {
this.setCurrentLanguageCode(lang);
this.routeService.removeQueryParam('lang');
}
});
}
Expand Down
31 changes: 31 additions & 0 deletions src/app/core/rest-property/forgot-password-check-guard.guard.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, Router, RouterStateSnapshot } from '@angular/router';
import { Observable, of } from 'rxjs';
import { AuthorizationDataService } from '../data/feature-authorization/authorization-data.service';
import { FeatureID } from '../data/feature-authorization/feature-id';
import {
SingleFeatureAuthorizationGuard
} from '../data/feature-authorization/feature-authorization-guard/single-feature-authorization.guard';
import { AuthService } from '../auth/auth.service';

@Injectable({
providedIn: 'root'
})
/**
* Guard that checks if the forgot-password feature is enabled
*/
export class ForgotPasswordCheckGuard extends SingleFeatureAuthorizationGuard {

constructor(
protected readonly authorizationService: AuthorizationDataService,
protected readonly router: Router,
protected readonly authService: AuthService
) {
super(authorizationService, router, authService);
}

getFeatureID(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<FeatureID> {
return of(FeatureID.EPersonForgotPassword);
}

}
19 changes: 19 additions & 0 deletions src/app/core/services/route.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -225,4 +225,23 @@ export class RouteService {
}
);
}

/**
* Remove a parameter from the current route
* @param key The parameter name
*/
removeQueryParam(key: string) {
let queryParams = { ...this.route.snapshot.queryParams };
delete queryParams[key];

// Navigate to the same route with the updated queryParams
this.router.navigate(
[],
{
relativeTo: this.route,
queryParams: queryParams,
}
);

}
}
2 changes: 1 addition & 1 deletion src/app/shared/cookies/browser-klaro.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -264,7 +264,7 @@ export class BrowserKlaroService extends KlaroService {
* Show the cookie consent form
*/
showSettings() {
this.lazyKlaro.then(({show}) => show(this.klaroConfig));
void this.lazyKlaro.then(({show}) => show(this.klaroConfig, true));
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -323,7 +323,7 @@ export class DsDynamicOneboxComponent extends DsDynamicVocabularyComponent imple
this.previousValue = result;
this.cdr.detectChanges();
}
if (hasValue(this.currentValue.otherInformation)) {
if (hasValue(this.currentValue?.otherInformation)) {
const infoKeys = Object.keys(this.currentValue.otherInformation);
this.setMultipleValuesForOtherInfo(infoKeys, this.currentValue);
}
Expand Down
11 changes: 8 additions & 3 deletions src/app/shared/log-in/log-in.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,12 @@
<ds-log-in-container [authMethod]="authMethod" [isStandalonePage]="isStandalonePage"></ds-log-in-container>
<div *ngIf="!last" class="dropdown-divider my-2"></div>
</ng-container>
<div class="dropdown-divider"></div>
<a class="dropdown-item" *ngIf="(canRegister$ | async) && showRegisterLink" [routerLink]="[getRegisterRoute()]" [attr.data-test]="'register' | dsBrowserOnly">{{"login.form.new-user" | translate}}</a>
<a class="dropdown-item" [routerLink]="[getForgotRoute()]" [attr.data-test]="'forgot' | dsBrowserOnly">{{"login.form.forgot-password" | translate}}</a>
<ng-container *ngIf="canShowDivider$ | async">
<div class="mt-2">
<a class="dropdown-item" *ngIf="canRegister$ | async" [routerLink]="[getRegisterRoute()]"
[attr.data-test]="'register' | dsBrowserOnly">{{"login.form.new-user" | translate}}</a>
<a class="dropdown-item" *ngIf="canForgot$ | async" [routerLink]="[getForgotRoute()]"
[attr.data-test]="'forgot' | dsBrowserOnly">{{"login.form.forgot-password" | translate}}</a>
</div>
</ng-container>
</div>
23 changes: 20 additions & 3 deletions src/app/shared/log-in/log-in.component.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { ChangeDetectionStrategy, Component, Input, OnDestroy, OnInit } from '@angular/core';

import { Observable, Subscription } from 'rxjs';
import { map } from 'rxjs/operators';
import { Observable, Subscription, combineLatest } from 'rxjs';
import { filter, map, shareReplay } from 'rxjs/operators';
import { select, Store } from '@ngrx/store';
import uniqBy from 'lodash/uniqBy';

Expand Down Expand Up @@ -67,6 +66,16 @@ export class LogInComponent implements OnInit, OnDestroy {
*/
canRegister$: Observable<boolean>;

/**
* Whether or not the current user (or anonymous) is authorized to register an account
*/
canForgot$: Observable<boolean>;

/**
* Shows the divider only if contains at least one link to show
*/
canShowDivider$: Observable<boolean>;

/**
* Track subscription to unsubscribe on destroy
* @private
Expand Down Expand Up @@ -106,6 +115,14 @@ export class LogInComponent implements OnInit, OnDestroy {
});

this.canRegister$ = this.authorizationService.isAuthorized(FeatureID.EPersonRegistration);

this.canForgot$ = this.authorizationService.isAuthorized(FeatureID.EPersonForgotPassword).pipe(shareReplay(1));
this.canShowDivider$ =
combineLatest([this.canRegister$, this.canForgot$])
.pipe(
map(([canRegister, canForgot]) => canRegister || canForgot),
filter(Boolean)
);
}

getRegisterRoute() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ import { AuthService } from '../../../../core/auth/auth.service';
import { HardRedirectService } from '../../../../core/services/hard-redirect.service';
import { CoreState } from '../../../../core/core-state.model';
import { getForgotPasswordRoute, getRegisterRoute } from '../../../../app-routing-paths';
import { FeatureID } from '../../../../core/data/feature-authorization/feature-id';
import { AuthorizationDataService } from '../../../../core/data/feature-authorization/authorization-data.service';
import { FeatureID } from '../../../../core/data/feature-authorization/feature-id';

/**
* /users/sign-in
Expand Down Expand Up @@ -73,6 +73,7 @@ export class LogInPasswordComponent implements OnInit {
*/
public canRegister$: Observable<boolean>;


constructor(
@Inject('authMethodProvider') public injectedAuthMethodModel: AuthMethod,
@Inject('isStandalonePage') public isStandalonePage: boolean,
Expand Down
3 changes: 3 additions & 0 deletions src/app/shared/testing/route-service.stub.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ export const routeServiceStub: any = {
},
getPreviousUrl: () => {
return observableOf('/home');
},
removeQueryParam: () => {
return;
}
/* eslint-enable no-empty, @typescript-eslint/no-empty-function */
};
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import {
mockSubmissionCollectionId,
mockSubmissionId,
mockSubmissionObject,
mockUploadResponse1ParsedErrors,
mockUploadResponse2Errors,
mockUploadResponse2ParsedErrors
} from '../../../shared/mocks/submission.mock';
Expand Down Expand Up @@ -154,17 +153,21 @@ describe('SubmissionUploadFilesComponent Component', () => {
compAsAny.uploadEnabled = observableOf(true);
});

it('should show a success notification and call updateSectionData if successful', () => {
const expectedErrors: any = mockUploadResponse1ParsedErrors;
it('should show a success notification and call updateSectionData if successful and no errors are present', () => {
const expectedErrors: any = [];
fixture.detectChanges();
const data = {
upload: {
files: [{url: 'testUrl'}]
}};

comp.onCompleteItem(Object.assign({}, uploadRestResponse, { sections: mockSectionsData }));
comp.onCompleteItem(Object.assign({}, uploadRestResponse, { sections: data }));

Object.keys(mockSectionsData).forEach((sectionId) => {
Object.keys(data).forEach((sectionId) => {
expect(sectionsServiceStub.updateSectionData).toHaveBeenCalledWith(
submissionId,
sectionId,
mockSectionsData[sectionId],
data[sectionId],
expectedErrors[sectionId],
expectedErrors[sectionId]
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@ import parseSectionErrors from '../../utils/parseSectionErrors';
import { SubmissionJsonPatchOperationsService } from '../../../core/submission/submission-json-patch-operations.service';
import { WorkspaceItem } from '../../../core/submission/models/workspaceitem.model';
import { SectionsType } from '../../sections/sections-type';
import { SubmissionSectionError } from '../../objects/submission-section-error.model';
import { isNumeric } from '../../../shared/numeric.util';
import { difference } from '../../../shared/object.util';
import {
WorkspaceitemSectionUploadFileObject
} from '../../../core/submission/models/workspaceitem-section-upload-file.model';

/**
* This component represents the drop zone that provides to add files to the submission.
Expand Down Expand Up @@ -130,17 +136,24 @@ export class SubmissionUploadFilesComponent implements OnChanges {
Object.keys(sections)
.forEach((sectionId) => {
const sectionData = normalizeSectionData(sections[sectionId]);
const sectionWarning = hasValue(sectionData.files) ? this.parseErrorsForWarning(sectionData.files, errorsList[sectionId]) : [];
const errorsForErrorNotification = difference(errorsList[sectionId], sectionWarning) as SubmissionSectionError[];
const sectionErrors = errorsList[sectionId];

this.sectionService.isSectionType(this.submissionId, sectionId, SectionsType.Upload)
.pipe(take(1))
.subscribe((isUpload) => {
if (isUpload) {
// Look for errors on upload
if ((isEmpty(sectionErrors))) {
if ((isEmpty(sectionErrors)) || !isEmpty(sectionWarning)) {
this.notificationsService.success(null, this.translate.get('submission.sections.upload.upload-successful'));
} else {
} else if (!isEmpty(errorsForErrorNotification)) {
this.notificationsService.error(null, this.translate.get('submission.sections.upload.upload-failed'));
}

if (!(isEmpty(sectionWarning))) {
this.notificationsService.warning(null, this.translate.get('submission.sections.upload.upload-warning'));
}
}
});
this.sectionService.updateSectionData(this.submissionId, sectionId, sectionData, sectionErrors, sectionErrors);
Expand All @@ -167,4 +180,22 @@ export class SubmissionUploadFilesComponent implements OnChanges {
.filter((subscription) => hasValue(subscription))
.forEach((subscription) => subscription.unsubscribe());
}

/**
* Check if there are errors related to metadata connected to files successfully uploaded
*
* @param files
* @param sectionErrors
* @private
*/
private parseErrorsForWarning(files: WorkspaceitemSectionUploadFileObject[], sectionErrors: SubmissionSectionError[]): SubmissionSectionError[] {
return !isEmpty(sectionErrors) ? sectionErrors.filter((error) => {
const errorPaths = error.path.split('/');
const errorIndexPart = errorPaths[errorPaths.length - 1];
// if index is not a number means that only one item is present
const errorIndex = isNumeric(errorIndexPart) ? parseInt(errorIndexPart, 10) : 0;
//we check if the files as an url with value, meaning the upload has been successfull
return hasValue(files[errorIndex]) && hasValue(files[errorIndex].url);
}) : [];
}
}
12 changes: 11 additions & 1 deletion src/app/submission/objects/submission-objects.reducer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -927,12 +927,20 @@ function editFileData(state: SubmissionObjectState, action: EditFileDataAction):
*/
function deleteFile(state: SubmissionObjectState, action: DeleteUploadedFileAction): SubmissionObjectState {
const filesData = state[ action.payload.submissionId ].sections[ action.payload.sectionId ].data as WorkspaceitemSectionUploadObject;
const filesErrorsToShow = state[ action.payload.submissionId ].sections[ action.payload.sectionId ].errorsToShow ?? [];
const filesSeverValidationErrors = state[ action.payload.submissionId ].sections[ action.payload.sectionId ].serverValidationErrors ?? [];

if (hasValue(filesData.files)) {
const fileIndex: any = findKey(
filesData.files,
{uuid: action.payload.fileId});
if (isNotNull(fileIndex)) {
const newData = Array.from(filesData.files);
const newErrorsToShow = filesData.files.length > 1 ? filesErrorsToShow
.filter(errorToShow => !errorToShow.path.includes(fileIndex)) : [];
const newServerErrorsToShow = filesData.files.length > 1 ? filesSeverValidationErrors
.filter(serverError => !serverError.path.includes(fileIndex)) : [];

newData.splice(fileIndex, 1);
return Object.assign({}, state, {
[ action.payload.submissionId ]: Object.assign({}, state[action.payload.submissionId], {
Expand All @@ -941,7 +949,9 @@ function deleteFile(state: SubmissionObjectState, action: DeleteUploadedFileActi
[ action.payload.sectionId ]: Object.assign({}, state[ action.payload.submissionId ].sections[ action.payload.sectionId ], {
data: Object.assign({}, state[ action.payload.submissionId ].sections[ action.payload.sectionId ].data, {
files: newData
})
}),
errorsToShow: newErrorsToShow,
serverValidationErrors: newServerErrorsToShow,
})
})
)
Expand Down
50 changes: 32 additions & 18 deletions src/app/submission/sections/sections.directive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,12 @@ export class SectionsDirective implements OnDestroy, OnInit {
*/
private subs: Subscription[] = [];

/**
* Dedicated properties for error checking
* @private
*/
private errorsSub: Subscription;

/**
* A boolean representing if section is valid
* @type {boolean}
Expand Down Expand Up @@ -119,29 +125,16 @@ export class SectionsDirective implements OnDestroy, OnInit {
map((valid: boolean) => {
if (valid) {
this.resetErrors();
} else if (hasValue(this.errorsSub)) {
//create new subscription to set any possible remaining error, so to avoid timing issue in status update
this.errorsSub.unsubscribe();
this.checkForNewErrors();
}
return valid;
}));

this.errorsSub = this.checkForNewErrors();
this.subs.push(
this.sectionService.getShownSectionErrors(this.submissionId, this.sectionId, this.sectionType)
.subscribe((errors: SubmissionSectionError[]) => {
if (isNotEmpty(errors)) {
errors.forEach((errorItem: SubmissionSectionError) => {
const parsedErrors: SectionErrorPath[] = parseSectionErrorPaths(errorItem.path);

parsedErrors.forEach((error: SectionErrorPath) => {
if (!error.fieldId) {
this.genericSectionErrors = uniq(this.genericSectionErrors.concat(errorItem.message));
} else {
this.allSectionErrors.push(errorItem.message);
}
});
});
} else {
this.resetErrors();
}
}),
this.submissionService.getActiveSectionId(this.submissionId)
.subscribe((activeSectionId) => {
const previousActive = this.active;
Expand Down Expand Up @@ -344,4 +337,25 @@ export class SectionsDirective implements OnDestroy, OnInit {
this.allSectionErrors = [];

}

private checkForNewErrors() {
return this.sectionService.getShownSectionErrors(this.submissionId, this.sectionId, this.sectionType)
.subscribe((errors: SubmissionSectionError[]) => {
if (isNotEmpty(errors)) {
errors.forEach((errorItem: SubmissionSectionError) => {
const parsedErrors: SectionErrorPath[] = parseSectionErrorPaths(errorItem.path);

parsedErrors.forEach((error: SectionErrorPath) => {
if (!error.fieldId) {
this.genericSectionErrors = uniq(this.genericSectionErrors.concat(errorItem.message));
} else {
this.allSectionErrors.push(errorItem.message);
}
});
});
} else {
this.resetErrors();
}
});
}
}
Loading

0 comments on commit 0c1a726

Please sign in to comment.