Skip to content

Commit 7b8371d

Browse files
authored
Merge branch 'main' into patch-1
2 parents c2376b6 + 60c317c commit 7b8371d

File tree

95 files changed

+6477
-438
lines changed

Some content is hidden

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

95 files changed

+6477
-438
lines changed

config/config.example.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,9 @@ languages:
135135
- code: lv
136136
label: Latviešu
137137
active: true
138+
- code: hi
139+
label: Hindi
140+
active: true
138141
- code: hu
139142
label: Magyar
140143
active: true

package.json

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,8 @@
104104
"jwt-decode": "^3.1.2",
105105
"klaro": "^0.7.10",
106106
"lodash": "^4.17.21",
107+
"markdown-it": "^13.0.1",
108+
"markdown-it-mathjax3": "^4.3.1",
107109
"mirador": "^3.3.0",
108110
"mirador-dl-plugin": "^0.13.0",
109111
"mirador-share-plugin": "^0.11.0",
@@ -116,20 +118,21 @@
116118
"ngx-moment": "^5.0.0",
117119
"ngx-pagination": "5.0.0",
118120
"ngx-sortablejs": "^11.1.0",
121+
"ngx-ui-switch": "^11.0.1",
119122
"nouislider": "^14.6.3",
120123
"pem": "1.14.4",
121124
"postcss-cli": "^9.1.0",
122125
"prop-types": "^15.7.2",
123126
"react-copy-to-clipboard": "^5.0.1",
124127
"reflect-metadata": "^0.1.13",
125128
"rxjs": "^7.5.5",
129+
"sanitize-html": "^2.7.2",
126130
"sortablejs": "1.13.0",
127131
"tslib": "^2.0.0",
128132
"url-parse": "^1.5.6",
129133
"uuid": "^8.3.2",
130134
"webfontloader": "1.6.28",
131-
"zone.js": "~0.11.5",
132-
"ngx-ui-switch": "^11.0.1"
135+
"zone.js": "~0.11.5"
133136
},
134137
"devDependencies": {
135138
"@angular-builders/custom-webpack": "~13.1.0",
@@ -155,6 +158,7 @@
155158
"@types/js-cookie": "2.2.6",
156159
"@types/lodash": "^4.14.165",
157160
"@types/node": "^14.14.9",
161+
"@types/sanitize-html": "^2.6.2",
158162
"@typescript-eslint/eslint-plugin": "5.11.0",
159163
"@typescript-eslint/parser": "5.11.0",
160164
"axe-core": "^4.3.3",

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

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,6 @@ export class EPeopleRegistryComponent implements OnInit, OnDestroy {
238238
this.epersonService.deleteEPerson(ePerson).pipe(getFirstCompletedRemoteData()).subscribe((restResponse: RemoteData<NoContent>) => {
239239
if (restResponse.hasSucceeded) {
240240
this.notificationsService.success(this.translateService.get(this.labelPrefix + 'notification.deleted.success', {name: ePerson.name}));
241-
this.reset();
242241
} else {
243242
this.notificationsService.error('Error occured when trying to delete EPerson with id: ' + ePerson.id + ' with code: ' + restResponse.statusCode + ' and message: ' + restResponse.errorMessage);
244243
}

src/app/access-control/group-registry/groups-registry.component.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,12 @@ import { TranslateService } from '@ngx-translate/core';
55
import {
66
BehaviorSubject,
77
combineLatest as observableCombineLatest,
8+
EMPTY,
89
Observable,
910
of as observableOf,
1011
Subscription
1112
} from 'rxjs';
12-
import { catchError, map, switchMap, tap } from 'rxjs/operators';
13+
import { catchError, defaultIfEmpty, map, switchMap, tap } from 'rxjs/operators';
1314
import { DSpaceObjectDataService } from '../../core/data/dspace-object-data.service';
1415
import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service';
1516
import { FeatureID } from '../../core/data/feature-authorization/feature-id';
@@ -144,7 +145,7 @@ export class GroupsRegistryComponent implements OnInit, OnDestroy {
144145
}
145146
return this.authorizationService.isAuthorized(FeatureID.AdministratorOf).pipe(
146147
switchMap((isSiteAdmin: boolean) => {
147-
return observableCombineLatest(groups.page.map((group: Group) => {
148+
return observableCombineLatest([...groups.page.map((group: Group) => {
148149
if (hasValue(group) && !this.deletedGroupsIds.includes(group.id)) {
149150
return observableCombineLatest([
150151
this.authorizationService.isAuthorized(FeatureID.CanDelete, group.self),
@@ -165,8 +166,10 @@ export class GroupsRegistryComponent implements OnInit, OnDestroy {
165166
}
166167
)
167168
);
169+
} else {
170+
return EMPTY;
168171
}
169-
})).pipe(map((dtos: GroupDtoModel[]) => {
172+
})]).pipe(defaultIfEmpty([]), map((dtos: GroupDtoModel[]) => {
170173
return buildPaginatedList(groups.pageInfo, dtos);
171174
}));
172175
})
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<div class="container">
2+
<h2 id="header">{{'admin.batch-import.page.header' | translate}}</h2>
3+
<p>{{'admin.batch-import.page.help' | translate}}</p>
4+
<p *ngIf="dso">
5+
selected collection: <b>{{getDspaceObjectName()}}</b>&nbsp;
6+
<a href="javascript:void(0)" (click)="removeDspaceObject()">{{'admin.batch-import.page.remove' | translate}}</a>
7+
</p>
8+
<p>
9+
<button class="btn btn-primary" (click)="this.selectCollection();">{{'admin.metadata-import.page.button.select-collection' | translate}}</button>
10+
</p>
11+
<div class="form-group">
12+
<div class="form-check">
13+
<input class="form-check-input" type="checkbox" id="validateOnly" [(ngModel)]="validateOnly">
14+
<label class="form-check-label" for="validateOnly">
15+
{{'admin.metadata-import.page.validateOnly' | translate}}
16+
</label>
17+
</div>
18+
<small id="validateOnlyHelpBlock" class="form-text text-muted">
19+
{{'admin.batch-import.page.validateOnly.hint' | translate}}
20+
</small>
21+
</div>
22+
23+
<ds-file-dropzone-no-uploader
24+
(onFileAdded)="setFile($event)"
25+
[dropMessageLabel]="'admin.batch-import.page.dropMsg'"
26+
[dropMessageLabelReplacement]="'admin.batch-import.page.dropMsgReplace'">
27+
</ds-file-dropzone-no-uploader>
28+
29+
<div class="space-children-mr">
30+
<button class="btn btn-secondary" id="backButton"
31+
(click)="this.onReturn();">{{'admin.metadata-import.page.button.return' | translate}}</button>
32+
<button class="btn btn-primary" id="proceedButton"
33+
(click)="this.importMetadata();">{{'admin.metadata-import.page.button.proceed' | translate}}</button>
34+
</div>
35+
</div>
Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
import { ComponentFixture, fakeAsync, TestBed, waitForAsync } from '@angular/core/testing';
2+
import { BatchImportPageComponent } from './batch-import-page.component';
3+
import { NotificationsServiceStub } from '../../shared/testing/notifications-service.stub';
4+
import { createFailedRemoteDataObject$, createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils';
5+
import { FormsModule } from '@angular/forms';
6+
import { TranslateModule } from '@ngx-translate/core';
7+
import { RouterTestingModule } from '@angular/router/testing';
8+
import { FileValueAccessorDirective } from '../../shared/utils/file-value-accessor.directive';
9+
import { FileValidator } from '../../shared/utils/require-file.validator';
10+
import { NotificationsService } from '../../shared/notifications/notifications.service';
11+
import {
12+
BATCH_IMPORT_SCRIPT_NAME,
13+
ScriptDataService
14+
} from '../../core/data/processes/script-data.service';
15+
import { Router } from '@angular/router';
16+
import { Location } from '@angular/common';
17+
import { NO_ERRORS_SCHEMA } from '@angular/core';
18+
import { By } from '@angular/platform-browser';
19+
import { ProcessParameter } from '../../process-page/processes/process-parameter.model';
20+
21+
describe('BatchImportPageComponent', () => {
22+
let component: BatchImportPageComponent;
23+
let fixture: ComponentFixture<BatchImportPageComponent>;
24+
25+
let notificationService: NotificationsServiceStub;
26+
let scriptService: any;
27+
let router;
28+
let locationStub;
29+
30+
function init() {
31+
notificationService = new NotificationsServiceStub();
32+
scriptService = jasmine.createSpyObj('scriptService',
33+
{
34+
invoke: createSuccessfulRemoteDataObject$({ processId: '46' })
35+
}
36+
);
37+
router = jasmine.createSpyObj('router', {
38+
navigateByUrl: jasmine.createSpy('navigateByUrl')
39+
});
40+
locationStub = jasmine.createSpyObj('location', {
41+
back: jasmine.createSpy('back')
42+
});
43+
}
44+
45+
beforeEach(waitForAsync(() => {
46+
init();
47+
TestBed.configureTestingModule({
48+
imports: [
49+
FormsModule,
50+
TranslateModule.forRoot(),
51+
RouterTestingModule.withRoutes([])
52+
],
53+
declarations: [BatchImportPageComponent, FileValueAccessorDirective, FileValidator],
54+
providers: [
55+
{ provide: NotificationsService, useValue: notificationService },
56+
{ provide: ScriptDataService, useValue: scriptService },
57+
{ provide: Router, useValue: router },
58+
{ provide: Location, useValue: locationStub },
59+
],
60+
schemas: [NO_ERRORS_SCHEMA]
61+
}).compileComponents();
62+
}));
63+
64+
beforeEach(() => {
65+
fixture = TestBed.createComponent(BatchImportPageComponent);
66+
component = fixture.componentInstance;
67+
fixture.detectChanges();
68+
});
69+
70+
it('should create', () => {
71+
expect(component).toBeTruthy();
72+
});
73+
74+
describe('if back button is pressed', () => {
75+
beforeEach(fakeAsync(() => {
76+
const proceed = fixture.debugElement.query(By.css('#backButton')).nativeElement;
77+
proceed.click();
78+
fixture.detectChanges();
79+
}));
80+
it('should do location.back', () => {
81+
expect(locationStub.back).toHaveBeenCalled();
82+
});
83+
});
84+
85+
describe('if file is set', () => {
86+
let fileMock: File;
87+
88+
beforeEach(() => {
89+
fileMock = new File([''], 'filename.zip', { type: 'application/zip' });
90+
component.setFile(fileMock);
91+
});
92+
93+
describe('if proceed button is pressed without validate only', () => {
94+
beforeEach(fakeAsync(() => {
95+
component.validateOnly = false;
96+
const proceed = fixture.debugElement.query(By.css('#proceedButton')).nativeElement;
97+
proceed.click();
98+
fixture.detectChanges();
99+
}));
100+
it('metadata-import script is invoked with --zip fileName and the mockFile', () => {
101+
const parameterValues: ProcessParameter[] = [
102+
Object.assign(new ProcessParameter(), { name: '--zip', value: 'filename.zip' }),
103+
];
104+
parameterValues.push(Object.assign(new ProcessParameter(), { name: '--add' }));
105+
expect(scriptService.invoke).toHaveBeenCalledWith(BATCH_IMPORT_SCRIPT_NAME, parameterValues, [fileMock]);
106+
});
107+
it('success notification is shown', () => {
108+
expect(notificationService.success).toHaveBeenCalled();
109+
});
110+
it('redirected to process page', () => {
111+
expect(router.navigateByUrl).toHaveBeenCalledWith('/processes/46');
112+
});
113+
});
114+
115+
describe('if proceed button is pressed with validate only', () => {
116+
beforeEach(fakeAsync(() => {
117+
component.validateOnly = true;
118+
const proceed = fixture.debugElement.query(By.css('#proceedButton')).nativeElement;
119+
proceed.click();
120+
fixture.detectChanges();
121+
}));
122+
it('metadata-import script is invoked with --zip fileName and the mockFile and -v validate-only', () => {
123+
const parameterValues: ProcessParameter[] = [
124+
Object.assign(new ProcessParameter(), { name: '--zip', value: 'filename.zip' }),
125+
Object.assign(new ProcessParameter(), { name: '--add' }),
126+
Object.assign(new ProcessParameter(), { name: '-v', value: true }),
127+
];
128+
expect(scriptService.invoke).toHaveBeenCalledWith(BATCH_IMPORT_SCRIPT_NAME, parameterValues, [fileMock]);
129+
});
130+
it('success notification is shown', () => {
131+
expect(notificationService.success).toHaveBeenCalled();
132+
});
133+
it('redirected to process page', () => {
134+
expect(router.navigateByUrl).toHaveBeenCalledWith('/processes/46');
135+
});
136+
});
137+
138+
describe('if proceed is pressed; but script invoke fails', () => {
139+
beforeEach(fakeAsync(() => {
140+
jasmine.getEnv().allowRespy(true);
141+
spyOn(scriptService, 'invoke').and.returnValue(createFailedRemoteDataObject$('Error', 500));
142+
const proceed = fixture.debugElement.query(By.css('#proceedButton')).nativeElement;
143+
proceed.click();
144+
fixture.detectChanges();
145+
}));
146+
it('error notification is shown', () => {
147+
expect(notificationService.error).toHaveBeenCalled();
148+
});
149+
});
150+
});
151+
});

0 commit comments

Comments
 (0)