Skip to content

Commit 2ac0bcd

Browse files
committed
Merge remote-tracking branch 'origin/main' into w2p-92900_Admin_options_dont_appear_after_Shibboleth_authentication_PR
2 parents 77eb6c1 + e7dc5f8 commit 2ac0bcd

44 files changed

Lines changed: 11518 additions & 8727 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.

.gitattributes

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,16 @@
1-
# Auto detect text files and perform LF normalization
1+
# By default, auto detect text files and perform LF normalization
2+
# This ensures code is always checked in with LF line endings
23
* text=auto
4+
5+
# JS and TS files must always use LF for Angular tools to work
6+
# Some Angular tools expect LF line endings, even on Windows.
7+
# This ensures Windows always checks out these files with LF line endings
8+
# We've copied many of these rules from https://github.com/angular/angular-cli/
9+
*.js eol=lf
10+
*.ts eol=lf
11+
*.json eol=lf
12+
*.json5 eol=lf
13+
*.css eol=lf
14+
*.scss eol=lf
15+
*.html eol=lf
16+
*.svg eol=lf

scripts/sync-i18n-files.ts

100755100644
File mode changed.

src/app/app.module.ts

100755100644
File mode changed.

src/app/menu.resolver.ts

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -16,24 +16,24 @@ import { filter, find, map, take } from 'rxjs/operators';
1616
import { hasValue } from './shared/empty.util';
1717
import { FeatureID } from './core/data/feature-authorization/feature-id';
1818
import {
19-
CreateCommunityParentSelectorComponent
20-
} from './shared/dso-selector/modal-wrappers/create-community-parent-selector/create-community-parent-selector.component';
19+
ThemedCreateCommunityParentSelectorComponent
20+
} from './shared/dso-selector/modal-wrappers/create-community-parent-selector/themed-create-community-parent-selector.component';
2121
import { OnClickMenuItemModel } from './shared/menu/menu-item/models/onclick.model';
2222
import {
23-
CreateCollectionParentSelectorComponent
24-
} from './shared/dso-selector/modal-wrappers/create-collection-parent-selector/create-collection-parent-selector.component';
23+
ThemedCreateCollectionParentSelectorComponent
24+
} from './shared/dso-selector/modal-wrappers/create-collection-parent-selector/themed-create-collection-parent-selector.component';
2525
import {
26-
CreateItemParentSelectorComponent
27-
} from './shared/dso-selector/modal-wrappers/create-item-parent-selector/create-item-parent-selector.component';
26+
ThemedCreateItemParentSelectorComponent
27+
} from './shared/dso-selector/modal-wrappers/create-item-parent-selector/themed-create-item-parent-selector.component';
2828
import {
29-
EditCommunitySelectorComponent
30-
} from './shared/dso-selector/modal-wrappers/edit-community-selector/edit-community-selector.component';
29+
ThemedEditCommunitySelectorComponent
30+
} from './shared/dso-selector/modal-wrappers/edit-community-selector/themed-edit-community-selector.component';
3131
import {
32-
EditCollectionSelectorComponent
33-
} from './shared/dso-selector/modal-wrappers/edit-collection-selector/edit-collection-selector.component';
32+
ThemedEditCollectionSelectorComponent
33+
} from './shared/dso-selector/modal-wrappers/edit-collection-selector/themed-edit-collection-selector.component';
3434
import {
35-
EditItemSelectorComponent
36-
} from './shared/dso-selector/modal-wrappers/edit-item-selector/edit-item-selector.component';
35+
ThemedEditItemSelectorComponent
36+
} from './shared/dso-selector/modal-wrappers/edit-item-selector/themed-edit-item-selector.component';
3737
import {
3838
ExportMetadataSelectorComponent
3939
} from './shared/dso-selector/modal-wrappers/export-metadata-selector/export-metadata-selector.component';
@@ -188,7 +188,7 @@ export class MenuResolver implements Resolve<boolean> {
188188
type: MenuItemType.ONCLICK,
189189
text: 'menu.section.new_community',
190190
function: () => {
191-
this.modalService.open(CreateCommunityParentSelectorComponent);
191+
this.modalService.open(ThemedCreateCommunityParentSelectorComponent);
192192
}
193193
} as OnClickMenuItemModel,
194194
},
@@ -201,7 +201,7 @@ export class MenuResolver implements Resolve<boolean> {
201201
type: MenuItemType.ONCLICK,
202202
text: 'menu.section.new_collection',
203203
function: () => {
204-
this.modalService.open(CreateCollectionParentSelectorComponent);
204+
this.modalService.open(ThemedCreateCollectionParentSelectorComponent);
205205
}
206206
} as OnClickMenuItemModel,
207207
},
@@ -214,7 +214,7 @@ export class MenuResolver implements Resolve<boolean> {
214214
type: MenuItemType.ONCLICK,
215215
text: 'menu.section.new_item',
216216
function: () => {
217-
this.modalService.open(CreateItemParentSelectorComponent);
217+
this.modalService.open(ThemedCreateItemParentSelectorComponent);
218218
}
219219
} as OnClickMenuItemModel,
220220
},
@@ -263,7 +263,7 @@ export class MenuResolver implements Resolve<boolean> {
263263
type: MenuItemType.ONCLICK,
264264
text: 'menu.section.edit_community',
265265
function: () => {
266-
this.modalService.open(EditCommunitySelectorComponent);
266+
this.modalService.open(ThemedEditCommunitySelectorComponent);
267267
}
268268
} as OnClickMenuItemModel,
269269
},
@@ -276,7 +276,7 @@ export class MenuResolver implements Resolve<boolean> {
276276
type: MenuItemType.ONCLICK,
277277
text: 'menu.section.edit_collection',
278278
function: () => {
279-
this.modalService.open(EditCollectionSelectorComponent);
279+
this.modalService.open(ThemedEditCollectionSelectorComponent);
280280
}
281281
} as OnClickMenuItemModel,
282282
},
@@ -289,7 +289,7 @@ export class MenuResolver implements Resolve<boolean> {
289289
type: MenuItemType.ONCLICK,
290290
text: 'menu.section.edit_item',
291291
function: () => {
292-
this.modalService.open(EditItemSelectorComponent);
292+
this.modalService.open(ThemedEditItemSelectorComponent);
293293
}
294294
} as OnClickMenuItemModel,
295295
},
Lines changed: 69 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,53 +1,99 @@
11
<div class="container" *ngVar="(processRD$ | async)?.payload as process">
22
<div class="d-flex">
3-
<h2 class="flex-grow-1">{{'process.detail.title' | translate:{id: process?.processId, name: process?.scriptName} }}</h2>
4-
<div>
5-
<button class="btn btn-lg btn-success " routerLink="/processes/new" [queryParams]="{id: process?.processId}"><i class="fas fa-plus pr-2"></i>{{'process.detail.create' | translate}}</button>
6-
</div>
3+
<h2 class="flex-grow-1">{{'process.detail.title' | translate:{
4+
id: process?.processId,
5+
name: process?.scriptName
6+
} }}</h2>
77
</div>
88
<ds-process-detail-field id="process-name" [title]="'process.detail.script'">
99
<div>{{ process?.scriptName }}</div>
1010
</ds-process-detail-field>
1111

12-
<ds-process-detail-field *ngIf="process?.parameters && process?.parameters?.length > 0" id="process-arguments" [title]="'process.detail.arguments'">
12+
<ds-process-detail-field *ngIf="process?.parameters && process?.parameters?.length > 0" id="process-arguments"
13+
[title]="'process.detail.arguments'">
1314
<div *ngFor="let argument of process?.parameters">{{ argument?.name }} {{ argument?.value }}</div>
1415
</ds-process-detail-field>
1516

1617
<div *ngVar="(filesRD$ | async)?.payload?.page as files">
17-
<ds-process-detail-field *ngIf="files && files?.length > 0" id="process-files" [title]="'process.detail.output-files'">
18-
<ds-file-download-link *ngFor="let file of files; let last=last;" [bitstream]="file">
19-
<span>{{getFileName(file)}}</span>
20-
<span>({{(file?.sizeBytes) | dsFileSize }})</span>
21-
</ds-file-download-link>
18+
<ds-process-detail-field *ngIf="files && files?.length > 0" id="process-files"
19+
[title]="'process.detail.output-files'">
20+
<ds-file-download-link *ngFor="let file of files; let last=last;" [bitstream]="file">
21+
<span>{{getFileName(file)}}</span>
22+
<span>({{(file?.sizeBytes) | dsFileSize }})</span>
23+
</ds-file-download-link>
2224
</ds-process-detail-field>
2325
</div>
2426

25-
<ds-process-detail-field *ngIf="process && process.startTime" id="process-start-time" [title]="'process.detail.start-time' | translate">
27+
<ds-process-detail-field *ngIf="process && process.startTime" id="process-start-time"
28+
[title]="'process.detail.start-time' | translate">
2629
<div>{{ process.startTime | date:dateFormat:'UTC' }}</div>
2730
</ds-process-detail-field>
2831

29-
<ds-process-detail-field *ngIf="process && process.endTime" id="process-end-time" [title]="'process.detail.end-time' | translate">
32+
<ds-process-detail-field *ngIf="process && process.endTime" id="process-end-time"
33+
[title]="'process.detail.end-time' | translate">
3034
<div>{{ process.endTime | date:dateFormat:'UTC' }}</div>
3135
</ds-process-detail-field>
3236

33-
<ds-process-detail-field *ngIf="process && process.processStatus" id="process-status" [title]="'process.detail.status' | translate">
37+
<ds-process-detail-field *ngIf="process && process.processStatus" id="process-status"
38+
[title]="'process.detail.status' | translate">
3439
<div>{{ process.processStatus }}</div>
3540
</ds-process-detail-field>
3641

3742
<ds-process-detail-field *ngIf="isProcessFinished(process)" id="process-output" [title]="'process.detail.output'">
38-
<button *ngIf="!showOutputLogs && process?._links?.output?.href != undefined" id="showOutputButton" class="btn btn-primary" (click)="showProcessOutputLogs()">
39-
{{ 'process.detail.logs.button' | translate }}
40-
</button>
41-
<ds-themed-loading *ngIf="retrievingOutputLogs$ | async" class="ds-themed-loading" message="{{ 'process.detail.logs.loading' | translate }}"></ds-themed-loading>
42-
<pre class="font-weight-bold text-secondary bg-light p-3"
43-
*ngIf="showOutputLogs && (outputLogs$ | async)?.length > 0">{{ (outputLogs$ | async) }}</pre>
44-
<p id="no-output-logs-message" *ngIf="(!(retrievingOutputLogs$ | async) && showOutputLogs)
43+
<button *ngIf="!showOutputLogs && process?._links?.output?.href != undefined" id="showOutputButton"
44+
class="btn btn-primary" (click)="showProcessOutputLogs()">
45+
{{ 'process.detail.logs.button' | translate }}
46+
</button>
47+
<ds-themed-loading *ngIf="retrievingOutputLogs$ | async" class="ds-themed-loading"
48+
message="{{ 'process.detail.logs.loading' | translate }}"></ds-themed-loading>
49+
<pre class="font-weight-bold text-secondary bg-light p-3"
50+
*ngIf="showOutputLogs && (outputLogs$ | async)?.length > 0">{{ (outputLogs$ | async) }}</pre>
51+
<p id="no-output-logs-message" *ngIf="(!(retrievingOutputLogs$ | async) && showOutputLogs)
4552
&& !(outputLogs$ | async) || (outputLogs$ | async)?.length == 0 || !process._links.output">
46-
{{ 'process.detail.logs.none' | translate }}
47-
</p>
53+
{{ 'process.detail.logs.none' | translate }}
54+
</p>
55+
</ds-process-detail-field>
56+
57+
<ds-process-detail-field id="process-actions" [title]="'process.detail.actions'">
58+
<button class="btn btn-success mr-2" routerLink="/processes/new" [queryParams]="{id: process?.processId}"><i
59+
class="fas fa-plus pr-2"></i>{{'process.detail.create' | translate}}</button>
60+
<button *ngIf="isProcessFinished(process)" id="delete" class="btn btn-danger"
61+
(click)="openDeleteModal(deleteModal)">
62+
<i class="fas fa-trash pr-2"></i>{{ 'process.detail.delete.button' | translate }}
63+
</button>
4864
</ds-process-detail-field>
4965

5066
<div style="text-align: right;">
51-
<a class="btn btn-outline-secondary mt-3" [routerLink]="'/processes'">{{'process.detail.back' | translate}}</a>
67+
<a class="btn btn-outline-secondary mt-3" [routerLink]="'/processes'">{{'process.detail.back' | translate}}</a>
5268
</div>
5369
</div>
70+
71+
<ng-template #deleteModal >
72+
73+
<div *ngVar="(processRD$ | async)?.payload as process">
74+
75+
<div class="modal-header">
76+
<div>
77+
<h4>{{'process.detail.delete.header' | translate }}</h4>
78+
</div>
79+
<button type="button" class="close"
80+
(click)="closeModal()" aria-label="Close">
81+
<span aria-hidden="true">×</span>
82+
</button>
83+
</div>
84+
85+
<div class="modal-body">
86+
<div>{{'process.detail.delete.body' | translate }}</div>
87+
<div class="mt-4">
88+
<button class="btn btn-primary mr-2" (click)="closeModal()">{{'process.detail.delete.cancel' | translate}}</button>
89+
<button id="delete-confirm" class="btn btn-danger"
90+
(click)="deleteProcess(process)">{{ 'process.detail.delete.confirm' | translate }}
91+
</button>
92+
</div>
93+
</div>
94+
95+
96+
</div>
97+
98+
</ng-template>
99+

src/app/process-page/detail/process-detail.component.spec.ts

Lines changed: 61 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,23 @@ import { RouterTestingModule } from '@angular/router/testing';
1919
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
2020
import { ProcessDetailFieldComponent } from './process-detail-field/process-detail-field.component';
2121
import { Process } from '../processes/process.model';
22-
import { ActivatedRoute } from '@angular/router';
22+
import { ActivatedRoute, Router } from '@angular/router';
2323
import { of as observableOf } from 'rxjs';
2424
import { By } from '@angular/platform-browser';
2525
import { FileSizePipe } from '../../shared/utils/file-size-pipe';
2626
import { Bitstream } from '../../core/shared/bitstream.model';
2727
import { ProcessDataService } from '../../core/data/processes/process-data.service';
2828
import { DSONameService } from '../../core/breadcrumbs/dso-name.service';
29-
import { createSuccessfulRemoteDataObject, createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils';
29+
import {
30+
createFailedRemoteDataObject$,
31+
createSuccessfulRemoteDataObject,
32+
createSuccessfulRemoteDataObject$
33+
} from '../../shared/remote-data.utils';
3034
import { createPaginatedList } from '../../shared/testing/utils.test';
35+
import { NotificationsServiceStub } from '../../shared/testing/notifications-service.stub';
36+
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
37+
import { NotificationsService } from '../../shared/notifications/notifications.service';
38+
import { getProcessListRoute } from '../process-page-routing.paths';
3139

3240
describe('ProcessDetailComponent', () => {
3341
let component: ProcessDetailComponent;
@@ -44,6 +52,11 @@ describe('ProcessDetailComponent', () => {
4452

4553
let processOutput;
4654

55+
let modalService;
56+
let notificationsService;
57+
58+
let router;
59+
4760
function init() {
4861
processOutput = 'Process Started';
4962
process = Object.assign(new Process(), {
@@ -93,7 +106,8 @@ describe('ProcessDetailComponent', () => {
93106
}
94107
});
95108
processService = jasmine.createSpyObj('processService', {
96-
getFiles: createSuccessfulRemoteDataObject$(createPaginatedList(files))
109+
getFiles: createSuccessfulRemoteDataObject$(createPaginatedList(files)),
110+
delete: createSuccessfulRemoteDataObject$(null)
97111
});
98112
bitstreamDataService = jasmine.createSpyObj('bitstreamDataService', {
99113
findByHref: createSuccessfulRemoteDataObject$(logBitstream)
@@ -104,13 +118,23 @@ describe('ProcessDetailComponent', () => {
104118
httpClient = jasmine.createSpyObj('httpClient', {
105119
get: observableOf(processOutput)
106120
});
121+
122+
modalService = jasmine.createSpyObj('modalService', {
123+
open: {}
124+
});
125+
126+
notificationsService = new NotificationsServiceStub();
127+
128+
router = jasmine.createSpyObj('router', {
129+
navigateByUrl:{}
130+
});
107131
}
108132

109133
beforeEach(waitForAsync(() => {
110134
init();
111135
TestBed.configureTestingModule({
112136
declarations: [ProcessDetailComponent, ProcessDetailFieldComponent, VarDirective, FileSizePipe],
113-
imports: [TranslateModule.forRoot(), RouterTestingModule.withRoutes([])],
137+
imports: [TranslateModule.forRoot()],
114138
providers: [
115139
{
116140
provide: ActivatedRoute,
@@ -121,6 +145,9 @@ describe('ProcessDetailComponent', () => {
121145
{ provide: DSONameService, useValue: nameService },
122146
{ provide: AuthService, useValue: new AuthServiceMock() },
123147
{ provide: HttpClient, useValue: httpClient },
148+
{ provide: NgbModal, useValue: modalService },
149+
{ provide: NotificationsService, useValue: notificationsService },
150+
{ provide: Router, useValue: router },
124151
],
125152
schemas: [CUSTOM_ELEMENTS_SCHEMA]
126153
}).compileComponents();
@@ -207,4 +234,34 @@ describe('ProcessDetailComponent', () => {
207234
});
208235
});
209236

237+
describe('openDeleteModal', () => {
238+
it('should open the modal', () => {
239+
component.openDeleteModal({});
240+
expect(modalService.open).toHaveBeenCalledWith({});
241+
});
242+
});
243+
244+
describe('deleteProcess', () => {
245+
it('should delete the process and navigate back to the overview page on success', () => {
246+
spyOn(component, 'closeModal');
247+
component.deleteProcess(process);
248+
249+
expect(processService.delete).toHaveBeenCalledWith(process.processId);
250+
expect(notificationsService.success).toHaveBeenCalled();
251+
expect(component.closeModal).toHaveBeenCalled();
252+
expect(router.navigateByUrl).toHaveBeenCalledWith(getProcessListRoute());
253+
});
254+
it('should delete the process and not navigate on error', () => {
255+
(processService.delete as jasmine.Spy).and.returnValue(createFailedRemoteDataObject$());
256+
spyOn(component, 'closeModal');
257+
258+
component.deleteProcess(process);
259+
260+
expect(processService.delete).toHaveBeenCalledWith(process.processId);
261+
expect(notificationsService.error).toHaveBeenCalled();
262+
expect(component.closeModal).not.toHaveBeenCalled();
263+
expect(router.navigateByUrl).not.toHaveBeenCalled();
264+
});
265+
});
266+
210267
});

0 commit comments

Comments
 (0)