Skip to content

Commit c8954da

Browse files
committed
Merge remote-tracking branch 'origin/main' into fix-versioning-button
2 parents 0f465ac + a9c58a1 commit c8954da

161 files changed

Lines changed: 5126 additions & 572 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

config/config.example.yml

Lines changed: 63 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,10 @@ submission:
131131
# NOTE: after how many time (milliseconds) submission is saved automatically
132132
# eg. timer: 5 * (1000 * 60); // 5 minutes
133133
timer: 0
134+
# Always show the duplicate detection section if enabled, even if there are no potential duplicates detected
135+
# (a message will be displayed to indicate no matches were found)
136+
duplicateDetection:
137+
alwaysShowSection: false
134138
icons:
135139
metadata:
136140
# NOTE: example of configuration
@@ -427,9 +431,67 @@ comcolSelectionSort:
427431

428432

429433
# Search settings
430-
search:
434+
search:
431435
# Settings to enable/disable or configure advanced search filters.
432436
advancedFilters:
433437
enabled: false
434438
# List of filters to enable in "Advanced Search" dropdown
435439
filter: [ 'title', 'author', 'subject', 'entityType' ]
440+
441+
442+
# Notify metrics
443+
# Configuration for Notify Admin Dashboard for metrics visualization
444+
notifyMetrics:
445+
# Configuration for received messages
446+
- title: 'admin-notify-dashboard.received-ldn'
447+
boxes:
448+
- color: '#B8DAFF'
449+
title: 'admin-notify-dashboard.NOTIFY.incoming.accepted'
450+
config: 'NOTIFY.incoming.accepted'
451+
description: 'admin-notify-dashboard.NOTIFY.incoming.accepted.description'
452+
- color: '#D4EDDA'
453+
title: 'admin-notify-dashboard.NOTIFY.incoming.processed'
454+
config: 'NOTIFY.incoming.processed'
455+
description: 'admin-notify-dashboard.NOTIFY.incoming.processed.description'
456+
- color: '#FDBBC7'
457+
title: 'admin-notify-dashboard.NOTIFY.incoming.failure'
458+
config: 'NOTIFY.incoming.failure'
459+
description: 'admin-notify-dashboard.NOTIFY.incoming.failure.description'
460+
- color: '#FDBBC7'
461+
title: 'admin-notify-dashboard.NOTIFY.incoming.untrusted'
462+
config: 'NOTIFY.incoming.untrusted'
463+
description: 'admin-notify-dashboard.NOTIFY.incoming.untrusted.description'
464+
- color: '#43515F'
465+
title: 'admin-notify-dashboard.NOTIFY.incoming.involvedItems'
466+
textColor: '#fff'
467+
config: 'NOTIFY.incoming.involvedItems'
468+
description: 'admin-notify-dashboard.NOTIFY.incoming.involvedItems.description'
469+
# Configuration for outgoing messages
470+
- title: 'admin-notify-dashboard.generated-ldn'
471+
boxes:
472+
- color: '#B8DAFF'
473+
title: 'admin-notify-dashboard.NOTIFY.outgoing.queued'
474+
config: 'NOTIFY.outgoing.queued'
475+
description: 'admin-notify-dashboard.NOTIFY.outgoing.queued.description'
476+
- color: '#FDEEBB'
477+
title: 'admin-notify-dashboard.NOTIFY.outgoing.queued_for_retry'
478+
config: 'NOTIFY.outgoing.queued_for_retry'
479+
description: 'admin-notify-dashboard.NOTIFY.outgoing.queued_for_retry.description'
480+
- color: '#FDBBC7'
481+
title: 'admin-notify-dashboard.NOTIFY.outgoing.failure'
482+
config: 'NOTIFY.outgoing.failure'
483+
description: 'admin-notify-dashboard.NOTIFY.outgoing.failure.description'
484+
- color: '#43515F'
485+
title: 'admin-notify-dashboard.NOTIFY.outgoing.involvedItems'
486+
textColor: '#fff'
487+
config: 'NOTIFY.outgoing.involvedItems'
488+
description: 'admin-notify-dashboard.NOTIFY.outgoing.involvedItems.description'
489+
- color: '#D4EDDA'
490+
title: 'admin-notify-dashboard.NOTIFY.outgoing.delivered'
491+
config: 'NOTIFY.outgoing.delivered'
492+
description: 'admin-notify-dashboard.NOTIFY.outgoing.delivered.description'
493+
494+
495+
496+
497+

karma.conf.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,10 @@ module.exports = function (config) {
1515
],
1616
client: {
1717
clearContext: false, // leave Jasmine Spec Runner output visible in browser
18-
captureConsole: false
18+
captureConsole: false,
19+
jasmine: {
20+
failSpecWithNoExpectations: true
21+
}
1922
},
2023
coverageIstanbulReporter: {
2124
dir: require('path').join(__dirname, './coverage/dspace-angular'),

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@
128128
"react-copy-to-clipboard": "^5.1.0",
129129
"reflect-metadata": "^0.1.13",
130130
"rxjs": "^7.8.0",
131-
"sanitize-html": "^2.10.0",
131+
"sanitize-html": "^2.12.1",
132132
"sortablejs": "1.15.0",
133133
"uuid": "^8.3.2",
134134
"webfontloader": "1.6.28",

src/app/access-control/epeople-registry/epeople-registry.component.spec.ts

Lines changed: 22 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ import { DebugElement, NO_ERRORS_SCHEMA } from '@angular/core';
55
import { ComponentFixture, fakeAsync, TestBed, tick, waitForAsync } from '@angular/core/testing';
66
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
77
import { BrowserModule, By } from '@angular/platform-browser';
8-
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
9-
import { TranslateLoader, TranslateModule, TranslateService } from '@ngx-translate/core';
8+
import { NgbModule, NgbModal } from '@ng-bootstrap/ng-bootstrap';
9+
import { TranslateModule } from '@ngx-translate/core';
1010
import { buildPaginatedList, PaginatedList } from '../../core/data/paginated-list.model';
1111
import { RemoteData } from '../../core/data/remote-data';
1212
import { EPersonDataService } from '../../core/eperson/eperson-data.service';
@@ -18,8 +18,6 @@ import { EPeopleRegistryComponent } from './epeople-registry.component';
1818
import { EPersonMock, EPersonMock2 } from '../../shared/testing/eperson.mock';
1919
import { createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils';
2020
import { getMockFormBuilderService } from '../../shared/mocks/form-builder-service.mock';
21-
import { getMockTranslateService } from '../../shared/mocks/translate.service.mock';
22-
import { TranslateLoaderMock } from '../../shared/mocks/translate-loader.mock';
2321
import { NotificationsServiceStub } from '../../shared/testing/notifications-service.stub';
2422
import { RouterStub } from '../../shared/testing/router.stub';
2523
import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service';
@@ -31,17 +29,15 @@ import { FindListOptions } from '../../core/data/find-list-options.model';
3129
describe('EPeopleRegistryComponent', () => {
3230
let component: EPeopleRegistryComponent;
3331
let fixture: ComponentFixture<EPeopleRegistryComponent>;
34-
let translateService: TranslateService;
3532
let builderService: FormBuilderService;
3633

37-
let mockEPeople;
34+
let mockEPeople: EPerson[];
3835
let ePersonDataServiceStub: any;
3936
let authorizationService: AuthorizationDataService;
40-
let modalService;
37+
let modalService: NgbModal;
38+
let paginationService: PaginationServiceStub;
4139

42-
let paginationService;
43-
44-
beforeEach(waitForAsync(() => {
40+
beforeEach(waitForAsync(async () => {
4541
jasmine.getEnv().allowRespy(true);
4642
mockEPeople = [EPersonMock, EPersonMock2];
4743
ePersonDataServiceStub = {
@@ -99,7 +95,7 @@ describe('EPeopleRegistryComponent', () => {
9995
deleteEPerson(ePerson: EPerson): Observable<boolean> {
10096
this.allEpeople = this.allEpeople.filter((ePerson2: EPerson) => {
10197
return (ePerson2.uuid !== ePerson.uuid);
102-
});
98+
});
10399
return observableOf(true);
104100
},
105101
editEPerson(ePerson: EPerson) {
@@ -119,17 +115,11 @@ describe('EPeopleRegistryComponent', () => {
119115
isAuthorized: observableOf(true)
120116
});
121117
builderService = getMockFormBuilderService();
122-
translateService = getMockTranslateService();
123118

124119
paginationService = new PaginationServiceStub();
125-
TestBed.configureTestingModule({
120+
await TestBed.configureTestingModule({
126121
imports: [CommonModule, NgbModule, FormsModule, ReactiveFormsModule, BrowserModule,
127-
TranslateModule.forRoot({
128-
loader: {
129-
provide: TranslateLoader,
130-
useClass: TranslateLoaderMock
131-
}
132-
}),
122+
TranslateModule.forRoot(),
133123
],
134124
declarations: [EPeopleRegistryComponent],
135125
providers: [
@@ -148,7 +138,7 @@ describe('EPeopleRegistryComponent', () => {
148138
beforeEach(() => {
149139
fixture = TestBed.createComponent(EPeopleRegistryComponent);
150140
component = fixture.componentInstance;
151-
modalService = (component as any).modalService;
141+
modalService = TestBed.inject(NgbModal);
152142
spyOn(modalService, 'open').and.returnValue(Object.assign({ componentInstance: Object.assign({ response: observableOf(true) }) }));
153143
fixture.detectChanges();
154144
});
@@ -158,18 +148,18 @@ describe('EPeopleRegistryComponent', () => {
158148
});
159149

160150
it('should display list of ePeople', () => {
161-
const ePeopleIdsFound = fixture.debugElement.queryAll(By.css('#epeople tr td:first-child'));
151+
const ePeopleIdsFound: DebugElement[] = fixture.debugElement.queryAll(By.css('#epeople tr td:first-child'));
162152
expect(ePeopleIdsFound.length).toEqual(2);
163153
mockEPeople.map((ePerson: EPerson) => {
164-
expect(ePeopleIdsFound.find((foundEl) => {
154+
expect(ePeopleIdsFound.find((foundEl: DebugElement) => {
165155
return (foundEl.nativeElement.textContent.trim() === ePerson.uuid);
166156
})).toBeTruthy();
167157
});
168158
});
169159

170160
describe('search', () => {
171161
describe('when searching with scope/query (scope metadata)', () => {
172-
let ePeopleIdsFound;
162+
let ePeopleIdsFound: DebugElement[];
173163
beforeEach(fakeAsync(() => {
174164
component.search({ scope: 'metadata', query: EPersonMock2.name });
175165
tick();
@@ -179,14 +169,14 @@ describe('EPeopleRegistryComponent', () => {
179169

180170
it('should display search result', () => {
181171
expect(ePeopleIdsFound.length).toEqual(1);
182-
expect(ePeopleIdsFound.find((foundEl) => {
172+
expect(ePeopleIdsFound.find((foundEl: DebugElement) => {
183173
return (foundEl.nativeElement.textContent.trim() === EPersonMock2.uuid);
184174
})).toBeTruthy();
185175
});
186176
});
187177

188178
describe('when searching with scope/query (scope email)', () => {
189-
let ePeopleIdsFound;
179+
let ePeopleIdsFound: DebugElement[];
190180
beforeEach(fakeAsync(() => {
191181
component.search({ scope: 'email', query: EPersonMock.email });
192182
tick();
@@ -196,7 +186,7 @@ describe('EPeopleRegistryComponent', () => {
196186

197187
it('should display search result', () => {
198188
expect(ePeopleIdsFound.length).toEqual(1);
199-
expect(ePeopleIdsFound.find((foundEl) => {
189+
expect(ePeopleIdsFound.find((foundEl: DebugElement) => {
200190
return (foundEl.nativeElement.textContent.trim() === EPersonMock.uuid);
201191
})).toBeTruthy();
202192
});
@@ -228,19 +218,12 @@ describe('EPeopleRegistryComponent', () => {
228218
});
229219
});
230220

231-
describe('delete EPerson button when the isAuthorized returns false', () => {
232-
let ePeopleDeleteButton;
233-
beforeEach(() => {
234-
spyOn(authorizationService, 'isAuthorized').and.returnValue(observableOf(false));
235-
component.initialisePage();
236-
fixture.detectChanges();
237-
});
238221

239-
it('should be disabled', () => {
240-
ePeopleDeleteButton = fixture.debugElement.queryAll(By.css('#epeople tr td div button.delete-button'));
241-
ePeopleDeleteButton.forEach((deleteButton: DebugElement) => {
242-
expect(deleteButton.nativeElement.disabled).toBe(true);
243-
});
244-
});
222+
it('should hide delete EPerson button when the isAuthorized returns false', () => {
223+
spyOn(authorizationService, 'isAuthorized').and.returnValue(observableOf(false));
224+
component.initialisePage();
225+
fixture.detectChanges();
226+
227+
expect(fixture.debugElement.query(By.css('#epeople tr td div button.delete-button'))).toBeNull();
245228
});
246229
});

src/app/admin/admin-ldn-services/ldn-service-form/ldn-service-form.component.html

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -102,8 +102,13 @@ <h1 class="flex-grow-1">{{ isNewService ? ('ldn-create-service.title' | translat
102102
id="ldnUrl"
103103
name="ldnUrl"
104104
type="text">
105-
<div *ngIf="formModel.get('ldnUrl').invalid && formModel.get('ldnUrl').touched" class="error-text">
106-
{{ 'ldn-new-service.form.error.ldnurl' | translate }}
105+
<div *ngIf="formModel.get('ldnUrl').invalid && formModel.get('ldnUrl').touched" >
106+
<div *ngIf="formModel.get('ldnUrl').errors['required']" class="error-text">
107+
{{ 'ldn-new-service.form.error.ldnurl' | translate }}
108+
</div>
109+
<div *ngIf="formModel.get('ldnUrl').errors['ldnUrlAlreadyAssociated']" class="error-text">
110+
{{ 'ldn-new-service.form.error.ldnurl.ldnUrlAlreadyAssociated' | translate }}
111+
</div>
107112
</div>
108113
</div>
109114

src/app/admin/admin-ldn-services/ldn-service-form/ldn-service-form.component.ts

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,12 @@ import {
66
TemplateRef,
77
ViewChild
88
} from '@angular/core';
9-
import {FormArray, FormBuilder, FormGroup, Validators} from '@angular/forms';
9+
import {
10+
FormArray,
11+
FormBuilder,
12+
FormGroup,
13+
Validators
14+
} from '@angular/forms';
1015
import {LDN_SERVICE} from '../ldn-services-model/ldn-service.resource-type';
1116
import {ActivatedRoute, Router} from '@angular/router';
1217
import {LdnServicesService} from '../ldn-services-data/ldn-services-data.service';
@@ -167,6 +172,9 @@ export class LdnServiceFormComponent implements OnInit, OnDestroy {
167172
this.closeModal();
168173
this.sendBack();
169174
} else {
175+
if (!this.formModel.errors) {
176+
this.setLdnUrlError();
177+
}
170178
this.notificationService.error(this.translateService.get('ldn-service-notification.created.failure.title'),
171179
this.translateService.get('ldn-service-notification.created.failure.body'));
172180
this.closeModal();
@@ -405,6 +413,9 @@ export class LdnServiceFormComponent implements OnInit, OnDestroy {
405413
this.notificationService.success(this.translateService.get('admin.registries.services-formats.modify.success.head'),
406414
this.translateService.get('admin.registries.services-formats.modify.success.content'));
407415
} else {
416+
if (!this.formModel.errors) {
417+
this.setLdnUrlError();
418+
}
408419
this.notificationService.error(this.translateService.get('admin.registries.services-formats.modify.failure.head'),
409420
this.translateService.get('admin.registries.services-formats.modify.failure.content'));
410421
this.closeModal();
@@ -554,4 +565,14 @@ export class LdnServiceFormComponent implements OnInit, OnDestroy {
554565
automatic: '',
555566
});
556567
}
568+
569+
570+
/**
571+
* set ldnUrl error in case of unprocessable entity and provided value
572+
*/
573+
private setLdnUrlError(): void {
574+
const control = this.formModel.controls.ldnUrl;
575+
const controlErrors = control.errors || {};
576+
control.setErrors({...controlErrors, ldnUrlAlreadyAssociated: true });
577+
}
557578
}

src/app/admin/admin-ldn-services/ldn-services-data/ldn-services-data.service.spec.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ describe('LdnServicesService test', () => {
7878

7979
rdbService = jasmine.createSpyObj('rdbService', {
8080
buildSingle: createSuccessfulRemoteDataObject$({}, 500),
81+
buildFromRequestUUID: createSuccessfulRemoteDataObject$({}, 500),
8182
buildList: cold('a', { a: remoteDataMocks.Success })
8283
});
8384

@@ -111,6 +112,20 @@ describe('LdnServicesService test', () => {
111112
done();
112113
});
113114
});
115+
116+
it('should invoke service', (done) => {
117+
const constraints = [{void: true}];
118+
const files = [new File([],'fileName')];
119+
spyOn(service as any, 'getInvocationFormData');
120+
spyOn(service, 'getBrowseEndpoint').and.returnValue(observableOf('testEndpoint'));
121+
service.invoke('serviceName', 'serviceId', constraints, files).subscribe(result => {
122+
expect((service as any).getInvocationFormData).toHaveBeenCalledWith(constraints, files);
123+
expect(service.getBrowseEndpoint).toHaveBeenCalled();
124+
expect(result).toBeInstanceOf(RemoteData);
125+
done();
126+
});
127+
128+
});
114129
});
115130

116131
});

0 commit comments

Comments
 (0)