mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-07 01:54:15 +00:00
Merge pull request #2019 from 4Science/CST-7755
Supervision orders user interface
This commit is contained in:
@@ -1 +1 @@
|
||||
<ds-configuration-search-page configuration="workflowAdmin" [context]="context"></ds-configuration-search-page>
|
||||
<ds-configuration-search-page configuration="supervision" [context]="context"></ds-configuration-search-page>
|
||||
|
@@ -4,24 +4,32 @@ import { NO_ERRORS_SCHEMA } from '@angular/core';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import { RouterTestingModule } from '@angular/router/testing';
|
||||
import { URLCombiner } from '../../../core/url-combiner/url-combiner';
|
||||
import { URLCombiner } from '../../../../../core/url-combiner/url-combiner';
|
||||
import { WorkflowItemAdminWorkflowActionsComponent } from './workflow-item-admin-workflow-actions.component';
|
||||
import { WorkflowItem } from '../../../core/submission/models/workflowitem.model';
|
||||
import { WorkflowItem } from '../../../../../core/submission/models/workflowitem.model';
|
||||
import {
|
||||
getWorkflowItemDeleteRoute,
|
||||
getWorkflowItemSendBackRoute
|
||||
} from '../../../workflowitems-edit-page/workflowitems-edit-page-routing-paths';
|
||||
} from '../../../../../workflowitems-edit-page/workflowitems-edit-page-routing-paths';
|
||||
import { of } from 'rxjs';
|
||||
import { Item } from '../../../../../core/shared/item.model';
|
||||
import { RemoteData } from '../../../../../core/data/remote-data';
|
||||
import { RequestEntryState } from '../../../../../core/data/request-entry-state.model';
|
||||
|
||||
describe('WorkflowItemAdminWorkflowActionsComponent', () => {
|
||||
let component: WorkflowItemAdminWorkflowActionsComponent;
|
||||
let fixture: ComponentFixture<WorkflowItemAdminWorkflowActionsComponent>;
|
||||
let id;
|
||||
let wfi;
|
||||
let item = new Item();
|
||||
item.uuid = 'itemUUID1111';
|
||||
const rd = new RemoteData(undefined, undefined, undefined, RequestEntryState.Success, undefined, item, 200);
|
||||
|
||||
function init() {
|
||||
id = '780b2588-bda5-4112-a1cd-0b15000a5339';
|
||||
wfi = new WorkflowItem();
|
||||
wfi.id = id;
|
||||
wfi.item = of(rd);
|
||||
}
|
||||
|
||||
beforeEach(waitForAsync(() => {
|
||||
@@ -59,4 +67,5 @@ describe('WorkflowItemAdminWorkflowActionsComponent', () => {
|
||||
const link = a.nativeElement.href;
|
||||
expect(link).toContain(new URLCombiner(getWorkflowItemSendBackRoute(wfi.id)).toString());
|
||||
});
|
||||
|
||||
});
|
@@ -1,9 +1,10 @@
|
||||
import { Component, Input } from '@angular/core';
|
||||
import { WorkflowItem } from '../../../core/submission/models/workflowitem.model';
|
||||
|
||||
import { WorkflowItem } from '../../../../../core/submission/models/workflowitem.model';
|
||||
import {
|
||||
getWorkflowItemSendBackRoute,
|
||||
getWorkflowItemDeleteRoute
|
||||
} from '../../../workflowitems-edit-page/workflowitems-edit-page-routing-paths';
|
||||
getWorkflowItemDeleteRoute,
|
||||
getWorkflowItemSendBackRoute
|
||||
} from '../../../../../workflowitems-edit-page/workflowitems-edit-page-routing-paths';
|
||||
|
||||
@Component({
|
||||
selector: 'ds-workflow-item-admin-workflow-actions-element',
|
||||
@@ -11,7 +12,7 @@ import {
|
||||
templateUrl: './workflow-item-admin-workflow-actions.component.html'
|
||||
})
|
||||
/**
|
||||
* The component for displaying the actions for a list element for an item on the admin workflow search page
|
||||
* The component for displaying the actions for a list element for a workflow-item on the admin workflow search page
|
||||
*/
|
||||
export class WorkflowItemAdminWorkflowActionsComponent {
|
||||
|
||||
@@ -21,7 +22,7 @@ export class WorkflowItemAdminWorkflowActionsComponent {
|
||||
@Input() public wfi: WorkflowItem;
|
||||
|
||||
/**
|
||||
* Whether or not to use small buttons
|
||||
* Whether to use small buttons or not
|
||||
*/
|
||||
@Input() public small: boolean;
|
||||
|
||||
@@ -29,7 +30,6 @@ export class WorkflowItemAdminWorkflowActionsComponent {
|
||||
* Returns the path to the delete page of this workflow item
|
||||
*/
|
||||
getDeleteRoute(): string {
|
||||
|
||||
return getWorkflowItemDeleteRoute(this.wfi.id);
|
||||
}
|
||||
|
||||
@@ -39,4 +39,5 @@ export class WorkflowItemAdminWorkflowActionsComponent {
|
||||
getSendBackRoute(): string {
|
||||
return getWorkflowItemSendBackRoute(this.wfi.id);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,44 @@
|
||||
<div>
|
||||
<div class="modal-header">{{'supervision-group-selector.header' | translate}}
|
||||
<button type="button" class="close" (click)="close()" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="row">
|
||||
<div class="control-group col-sm-12">
|
||||
<label for="supervisionOrder">{{'supervision-group-selector.select.type-of-order.label' | translate}}</label>
|
||||
<select name="supervisionOrder" id="supervisionOrder" class="form-control"
|
||||
[(ngModel)]="selectedOrderType"
|
||||
attr.aria-label="{{'supervision-group-selector.select.type-of-order.label' | translate}}">
|
||||
<option value="EDITOR">{{'supervision-group-selector.select.type-of-order.option.editor' | translate}}</option>
|
||||
<option value="OBSERVER">{{'supervision-group-selector.select.type-of-order.option.observer' | translate}}</option>
|
||||
</select>
|
||||
<ds-error *ngIf="isSubmitted && (!selectedOrderType || selectedOrderType === '')"
|
||||
message="{{'supervision-group-selector.select.type-of-order.error' | translate}}"></ds-error>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="control-group col-sm-12">
|
||||
<label for="supervisionGroup">{{'supervision-group-selector.select.group.label' | translate}}</label>
|
||||
<ng-container class="mb-3">
|
||||
<input id="supervisionGroup" class="form-control" type="text" [value]="selectedGroup ? dsoNameService.getName(selectedGroup) : ''" disabled>
|
||||
<ds-error *ngIf="isSubmitted && !selectedGroup" message="{{'supervision-group-selector.select.group.error' | translate}}"></ds-error>
|
||||
</ng-container>
|
||||
<ds-eperson-group-list [isListOfEPerson]="false"
|
||||
(select)="updateGroupObjectSelected($event)"></ds-eperson-group-list>
|
||||
</div>
|
||||
</div>
|
||||
<!-- <div class="d-flex flex-row-reverse m-2"> -->
|
||||
<div class="modal-footer">
|
||||
<button class="btn btn-outline-secondary"
|
||||
(click)="close()">
|
||||
<i class="fas fa-times"></i> {{"supervision-group-selector.button.cancel" | translate}}
|
||||
</button>
|
||||
<button class="btn btn-primary save"
|
||||
(click)="save()">
|
||||
<i class="fas fa-save"></i> {{"supervision-group-selector.button.save" | translate}}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@@ -0,0 +1,70 @@
|
||||
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { DebugElement, NO_ERRORS_SCHEMA } from '@angular/core';
|
||||
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
|
||||
import { SupervisionOrderGroupSelectorComponent } from './supervision-order-group-selector.component';
|
||||
import { SupervisionOrderDataService } from '../../../../../../core/supervision-order/supervision-order-data.service';
|
||||
import { NotificationsService } from '../../../../../../shared/notifications/notifications.service';
|
||||
import { Group } from '../../../../../../core/eperson/models/group.model';
|
||||
import { SupervisionOrder } from '../../../../../../core/supervision-order/models/supervision-order.model';
|
||||
import { of } from 'rxjs';
|
||||
|
||||
describe('SupervisionOrderGroupSelectorComponent', () => {
|
||||
let component: SupervisionOrderGroupSelectorComponent;
|
||||
let fixture: ComponentFixture<SupervisionOrderGroupSelectorComponent>;
|
||||
let debugElement: DebugElement;
|
||||
|
||||
const modalStub = jasmine.createSpyObj('modalStub', ['close']);
|
||||
|
||||
const supervisionOrderDataService: any = jasmine.createSpyObj('supervisionOrderDataService', {
|
||||
create: of(new SupervisionOrder())
|
||||
});
|
||||
|
||||
const selectedOrderType = 'NONE';
|
||||
const itemUUID = 'itemUUID1234';
|
||||
|
||||
const selectedGroup = new Group();
|
||||
selectedGroup.uuid = 'GroupUUID1234';
|
||||
|
||||
const supervisionDataObject = new SupervisionOrder();
|
||||
supervisionDataObject.ordertype = selectedOrderType;
|
||||
|
||||
beforeEach(waitForAsync(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [TranslateModule.forRoot()],
|
||||
declarations: [SupervisionOrderGroupSelectorComponent],
|
||||
providers: [
|
||||
{ provide: NgbActiveModal, useValue: modalStub },
|
||||
{ provide: SupervisionOrderDataService, useValue: supervisionOrderDataService },
|
||||
{ provide: NotificationsService, useValue: {} },
|
||||
],
|
||||
schemas: [NO_ERRORS_SCHEMA]
|
||||
}).compileComponents();
|
||||
|
||||
}));
|
||||
|
||||
beforeEach(waitForAsync(() => {
|
||||
fixture = TestBed.createComponent(SupervisionOrderGroupSelectorComponent);
|
||||
component = fixture.componentInstance;
|
||||
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
component.itemUUID = itemUUID;
|
||||
component.selectedGroup = selectedGroup;
|
||||
component.selectedOrderType = selectedOrderType;
|
||||
debugElement = fixture.debugElement;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create component', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should call create for supervision order', () => {
|
||||
component.save();
|
||||
fixture.detectChanges();
|
||||
expect(supervisionOrderDataService.create).toHaveBeenCalledWith(supervisionDataObject, itemUUID, selectedGroup.uuid, selectedOrderType);
|
||||
});
|
||||
|
||||
});
|
@@ -0,0 +1,97 @@
|
||||
import { Component, EventEmitter, Output } from '@angular/core';
|
||||
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { getFirstCompletedRemoteData } from 'src/app/core/shared/operators';
|
||||
import { NotificationsService } from 'src/app/shared/notifications/notifications.service';
|
||||
import { DSONameService } from '../../../../../../core/breadcrumbs/dso-name.service';
|
||||
import { Group } from '../../../../../../core/eperson/models/group.model';
|
||||
import { SupervisionOrder } from '../../../../../../core/supervision-order/models/supervision-order.model';
|
||||
import { SupervisionOrderDataService } from '../../../../../../core/supervision-order/supervision-order-data.service';
|
||||
import { RemoteData } from '../../../../../../core/data/remote-data';
|
||||
|
||||
/**
|
||||
* Component to wrap a dropdown - for type of order -
|
||||
* and a list of groups
|
||||
* inside a modal
|
||||
* Used to create a new supervision order
|
||||
*/
|
||||
|
||||
@Component({
|
||||
selector: 'ds-supervision-group-selector',
|
||||
styleUrls: ['./supervision-order-group-selector.component.scss'],
|
||||
templateUrl: './supervision-order-group-selector.component.html',
|
||||
})
|
||||
export class SupervisionOrderGroupSelectorComponent {
|
||||
|
||||
/**
|
||||
* The item to perform the actions on
|
||||
*/
|
||||
itemUUID: string;
|
||||
|
||||
/**
|
||||
* The selected supervision order type
|
||||
*/
|
||||
selectedOrderType: string;
|
||||
|
||||
/**
|
||||
* selected group for supervision
|
||||
*/
|
||||
selectedGroup: Group;
|
||||
|
||||
/**
|
||||
* boolean flag for the validations
|
||||
*/
|
||||
isSubmitted = false;
|
||||
|
||||
/**
|
||||
* Event emitted when a new SupervisionOrder has been created
|
||||
*/
|
||||
@Output() create: EventEmitter<SupervisionOrder> = new EventEmitter<SupervisionOrder>();
|
||||
|
||||
constructor(
|
||||
public dsoNameService: DSONameService,
|
||||
private activeModal: NgbActiveModal,
|
||||
private supervisionOrderDataService: SupervisionOrderDataService,
|
||||
protected notificationsService: NotificationsService,
|
||||
protected translateService: TranslateService,
|
||||
) { }
|
||||
|
||||
/**
|
||||
* Close the modal
|
||||
*/
|
||||
close() {
|
||||
this.activeModal.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Assign the value of group on select
|
||||
*/
|
||||
updateGroupObjectSelected(object) {
|
||||
this.selectedGroup = object;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the supervision order
|
||||
*/
|
||||
save() {
|
||||
this.isSubmitted = true;
|
||||
if (this.selectedOrderType && this.selectedGroup) {
|
||||
let supervisionDataObject = new SupervisionOrder();
|
||||
supervisionDataObject.ordertype = this.selectedOrderType;
|
||||
this.supervisionOrderDataService.create(supervisionDataObject, this.itemUUID, this.selectedGroup.uuid, this.selectedOrderType).pipe(
|
||||
getFirstCompletedRemoteData(),
|
||||
).subscribe((rd: RemoteData<SupervisionOrder>) => {
|
||||
if (rd.state === 'Success') {
|
||||
this.notificationsService.success(this.translateService.get('supervision-group-selector.notification.create.success.title', { name: this.selectedGroup.name }));
|
||||
this.create.emit(rd.payload);
|
||||
this.close();
|
||||
} else {
|
||||
this.notificationsService.error(
|
||||
this.translateService.get('supervision-group-selector.notification.create.failure.title'),
|
||||
rd.statusCode === 422 ? this.translateService.get('supervision-group-selector.notification.create.already-existing') : rd.errorMessage);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,15 @@
|
||||
<ng-container *ngVar="(supervisionOrderEntries$ | async) as supervisionOrders">
|
||||
<div class="item-list-supervision" *ngIf="supervisionOrders?.length > 0">
|
||||
<div>
|
||||
<span>{{'workflow-item.search.result.list.element.supervised-by' | translate}} </span>
|
||||
</div>
|
||||
<div>
|
||||
<a class="badge badge-primary mr-1 mb-1 text-capitalize mw-100 text-truncate" *ngFor="let supervisionOrder of supervisionOrders" data-test="soBadge"
|
||||
[ngbTooltip]="'workflow-item.search.result.list.element.supervised.remove-tooltip' | translate"
|
||||
(click)="$event.preventDefault(); $event.stopImmediatePropagation(); deleteSupervisionOrder(supervisionOrder)" aria-label="Close">
|
||||
{{supervisionOrder.group.name}}
|
||||
<span aria-hidden="true"> ×</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
@@ -0,0 +1,3 @@
|
||||
.badge {
|
||||
cursor: pointer;
|
||||
}
|
@@ -0,0 +1,61 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { NO_ERRORS_SCHEMA, SimpleChange } from '@angular/core';
|
||||
import { By } from '@angular/platform-browser';
|
||||
|
||||
import { NgbTooltipModule } from '@ng-bootstrap/ng-bootstrap';
|
||||
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
|
||||
|
||||
import { SupervisionOrderStatusComponent } from './supervision-order-status.component';
|
||||
import { VarDirective } from '../../../../../../shared/utils/var.directive';
|
||||
import { TranslateLoaderMock } from '../../../../../../shared/mocks/translate-loader.mock';
|
||||
import { supervisionOrderListMock } from '../../../../../../shared/testing/supervision-order.mock';
|
||||
|
||||
describe('SupervisionOrderStatusComponent', () => {
|
||||
let component: SupervisionOrderStatusComponent;
|
||||
let fixture: ComponentFixture<SupervisionOrderStatusComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [
|
||||
NgbTooltipModule,
|
||||
TranslateModule.forRoot({
|
||||
loader: {
|
||||
provide: TranslateLoader,
|
||||
useClass: TranslateLoaderMock
|
||||
}
|
||||
})
|
||||
],
|
||||
declarations: [ SupervisionOrderStatusComponent, VarDirective ],
|
||||
schemas: [
|
||||
NO_ERRORS_SCHEMA
|
||||
]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(SupervisionOrderStatusComponent);
|
||||
component = fixture.componentInstance;
|
||||
component.supervisionOrderList = supervisionOrderListMock;
|
||||
component.ngOnChanges( {
|
||||
supervisionOrderList: new SimpleChange(null, supervisionOrderListMock, true)
|
||||
});
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should render badges properly', () => {
|
||||
const badges = fixture.debugElement.queryAll(By.css('[data-test="soBadge"]'));
|
||||
expect(badges.length).toBe(2);
|
||||
});
|
||||
|
||||
it('should emit delete event on click', () => {
|
||||
spyOn(component.delete, 'emit');
|
||||
const badges = fixture.debugElement.queryAll(By.css('[data-test="soBadge"]'));
|
||||
badges[0].nativeElement.click();
|
||||
expect(component.delete.emit).toHaveBeenCalled();
|
||||
});
|
||||
});
|
@@ -0,0 +1,82 @@
|
||||
import { Component, EventEmitter, Input, OnChanges, Output, SimpleChanges } from '@angular/core';
|
||||
|
||||
import { BehaviorSubject, from, Observable } from 'rxjs';
|
||||
import { map, mergeMap, reduce } from 'rxjs/operators';
|
||||
|
||||
import { SupervisionOrder } from '../../../../../../core/supervision-order/models/supervision-order.model';
|
||||
import { Group } from '../../../../../../core/eperson/models/group.model';
|
||||
import { getFirstCompletedRemoteData } from '../../../../../../core/shared/operators';
|
||||
import { isNotEmpty } from '../../../../../../shared/empty.util';
|
||||
import { RemoteData } from '../../../../../../core/data/remote-data';
|
||||
|
||||
export interface SupervisionOrderListEntry {
|
||||
supervisionOrder: SupervisionOrder;
|
||||
group: Group
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'ds-supervision-order-status',
|
||||
templateUrl: './supervision-order-status.component.html',
|
||||
styleUrls: ['./supervision-order-status.component.scss']
|
||||
})
|
||||
export class SupervisionOrderStatusComponent implements OnChanges {
|
||||
|
||||
/**
|
||||
* The list of supervision order object to show
|
||||
*/
|
||||
@Input() supervisionOrderList: SupervisionOrder[] = [];
|
||||
|
||||
/**
|
||||
* List of the supervision orders combined with the group
|
||||
*/
|
||||
supervisionOrderEntries$: BehaviorSubject<SupervisionOrderListEntry[]> = new BehaviorSubject([]);
|
||||
|
||||
@Output() delete: EventEmitter<SupervisionOrderListEntry> = new EventEmitter<SupervisionOrderListEntry>();
|
||||
|
||||
ngOnChanges(changes: SimpleChanges): void {
|
||||
if (changes && changes.supervisionOrderList) {
|
||||
this.getSupervisionOrderEntries(changes.supervisionOrderList.currentValue)
|
||||
.subscribe((supervisionOrderEntries: SupervisionOrderListEntry[]) => {
|
||||
this.supervisionOrderEntries$.next(supervisionOrderEntries);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a list of SupervisionOrderListEntry by the given SupervisionOrder list
|
||||
*
|
||||
* @param supervisionOrderList
|
||||
*/
|
||||
private getSupervisionOrderEntries(supervisionOrderList: SupervisionOrder[]): Observable<SupervisionOrderListEntry[]> {
|
||||
return from(supervisionOrderList).pipe(
|
||||
mergeMap((so: SupervisionOrder) => so.group.pipe(
|
||||
getFirstCompletedRemoteData(),
|
||||
map((sogRD: RemoteData<Group>) => {
|
||||
if (sogRD.hasSucceeded) {
|
||||
const entry: SupervisionOrderListEntry = {
|
||||
supervisionOrder: so,
|
||||
group: sogRD.payload
|
||||
};
|
||||
return entry;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
})
|
||||
)),
|
||||
reduce((acc: SupervisionOrderListEntry[], value: any) => {
|
||||
if (isNotEmpty(value)) {
|
||||
return [...acc, value];
|
||||
} else {
|
||||
return acc;
|
||||
}
|
||||
}, []),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit a delete event with the given SupervisionOrderListEntry.
|
||||
*/
|
||||
deleteSupervisionOrder(supervisionOrder: SupervisionOrderListEntry) {
|
||||
this.delete.emit(supervisionOrder);
|
||||
}
|
||||
}
|
@@ -0,0 +1,16 @@
|
||||
<div class="my-1">
|
||||
<ds-supervision-order-status [supervisionOrderList]="supervisionOrderList"
|
||||
(delete)="deleteSupervisionOrder($event)"></ds-supervision-order-status>
|
||||
</div>
|
||||
|
||||
<div class="space-children-mr">
|
||||
<a [ngClass]="{'btn-sm': small}" class="btn btn-light my-1 delete-link" [routerLink]="[getDeleteRoute()]" [title]="'admin.workflow.item.delete' | translate">
|
||||
<i class="fa fa-trash"></i><span *ngIf="!small" class="d-none d-sm-inline"> {{"admin.workflow.item.delete" | translate}}</span>
|
||||
</a>
|
||||
<a [ngClass]="{'btn-sm': small}" class="btn btn-light my-1 policies-link" [routerLink]="resourcePoliciesPageRoute" [title]="'admin.workflow.item.policies' | translate">
|
||||
<i class="fas fa-edit"></i><span *ngIf="!small" class="d-none d-sm-inline"> {{'admin.workflow.item.policies' | translate}}</span>
|
||||
</a>
|
||||
<a [ngClass]="{'btn-sm': small}" class="btn btn-light my-1 supervision-group-selector" [title]="'admin.workflow.item.supervision' | translate" (click)="openSupervisionModal()">
|
||||
<i class="fas fa-users-cog"></i><span *ngIf="!small" class="d-none d-sm-inline"> {{'admin.workflow.item.supervision' | translate}}</span>
|
||||
</a>
|
||||
</div>
|
@@ -0,0 +1 @@
|
||||
|
@@ -0,0 +1,156 @@
|
||||
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
||||
import { NO_ERRORS_SCHEMA } from '@angular/core';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import { RouterTestingModule } from '@angular/router/testing';
|
||||
|
||||
import { of } from 'rxjs';
|
||||
import { NgbModalModule } from '@ng-bootstrap/ng-bootstrap';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
|
||||
import { URLCombiner } from '../../../../../core/url-combiner/url-combiner';
|
||||
import { WorkspaceItemAdminWorkflowActionsComponent } from './workspace-item-admin-workflow-actions.component';
|
||||
import { WorkspaceItem } from '../../../../../core/submission/models/workspaceitem.model';
|
||||
import {
|
||||
getWorkflowItemDeleteRoute,
|
||||
} from '../../../../../workflowitems-edit-page/workflowitems-edit-page-routing-paths';
|
||||
import { Item } from '../../../../../core/shared/item.model';
|
||||
import { RemoteData } from '../../../../../core/data/remote-data';
|
||||
import { RequestEntryState } from '../../../../../core/data/request-entry-state.model';
|
||||
import { NotificationsServiceStub } from '../../../../../shared/testing/notifications-service.stub';
|
||||
import { NotificationsService } from '../../../../../shared/notifications/notifications.service';
|
||||
import { DSONameService } from '../../../../../core/breadcrumbs/dso-name.service';
|
||||
import { DSONameServiceMock } from '../../../../../shared/mocks/dso-name.service.mock';
|
||||
import { SupervisionOrderDataService } from '../../../../../core/supervision-order/supervision-order-data.service';
|
||||
import { ConfirmationModalComponent } from '../../../../../shared/confirmation-modal/confirmation-modal.component';
|
||||
import { supervisionOrderEntryMock } from '../../../../../shared/testing/supervision-order.mock';
|
||||
import {
|
||||
SupervisionOrderGroupSelectorComponent
|
||||
} from './supervision-order-group-selector/supervision-order-group-selector.component';
|
||||
|
||||
describe('WorkspaceItemAdminWorkflowActionsComponent', () => {
|
||||
let component: WorkspaceItemAdminWorkflowActionsComponent;
|
||||
let fixture: ComponentFixture<WorkspaceItemAdminWorkflowActionsComponent>;
|
||||
let id;
|
||||
let wsi;
|
||||
let item = new Item();
|
||||
item.uuid = 'itemUUID1111';
|
||||
const rd = new RemoteData(undefined, undefined, undefined, RequestEntryState.Success, undefined, item, 200);
|
||||
let supervisionOrderDataService;
|
||||
let notificationService: NotificationsServiceStub;
|
||||
|
||||
function init() {
|
||||
notificationService = new NotificationsServiceStub();
|
||||
supervisionOrderDataService = jasmine.createSpyObj('supervisionOrderDataService', {
|
||||
searchByItem: jasmine.createSpy('searchByItem'),
|
||||
delete: jasmine.createSpy('delete'),
|
||||
});
|
||||
id = '780b2588-bda5-4112-a1cd-0b15000a5339';
|
||||
wsi = new WorkspaceItem();
|
||||
wsi.id = id;
|
||||
wsi.item = of(rd);
|
||||
}
|
||||
|
||||
beforeEach(waitForAsync(() => {
|
||||
init();
|
||||
TestBed.configureTestingModule({
|
||||
imports: [
|
||||
NgbModalModule,
|
||||
TranslateModule.forRoot(),
|
||||
RouterTestingModule.withRoutes([])
|
||||
],
|
||||
declarations: [WorkspaceItemAdminWorkflowActionsComponent],
|
||||
providers: [
|
||||
{ provide: DSONameService, useClass: DSONameServiceMock },
|
||||
{ provide: NotificationsService, useValue: notificationService },
|
||||
{ provide: SupervisionOrderDataService, useValue: supervisionOrderDataService }
|
||||
],
|
||||
schemas: [NO_ERRORS_SCHEMA]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(WorkspaceItemAdminWorkflowActionsComponent);
|
||||
component = fixture.componentInstance;
|
||||
component.wsi = wsi;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should render a delete button with the correct link', () => {
|
||||
const button = fixture.debugElement.query(By.css('a.delete-link'));
|
||||
const link = button.nativeElement.href;
|
||||
expect(link).toContain(new URLCombiner(getWorkflowItemDeleteRoute(wsi.id)).toString());
|
||||
});
|
||||
|
||||
it('should render a policies button with the correct link', () => {
|
||||
const a = fixture.debugElement.query(By.css('a.policies-link'));
|
||||
const link = a.nativeElement.href;
|
||||
expect(link).toContain(new URLCombiner('/items/itemUUID1111/edit/authorizations').toString());
|
||||
});
|
||||
|
||||
describe('deleteSupervisionOrder', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
spyOn(component.delete, 'emit');
|
||||
spyOn((component as any).modalService, 'open').and.returnValue({
|
||||
componentInstance: { response: of(true) }
|
||||
});
|
||||
});
|
||||
|
||||
describe('when delete succeeded', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
supervisionOrderDataService.delete.and.returnValue(of(true));
|
||||
});
|
||||
|
||||
it('should notify success', () => {
|
||||
component.deleteSupervisionOrder(supervisionOrderEntryMock);
|
||||
expect((component as any).modalService.open).toHaveBeenCalledWith(ConfirmationModalComponent);
|
||||
expect(notificationService.success).toHaveBeenCalled();
|
||||
expect(component.delete.emit).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('when delete failed', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
supervisionOrderDataService.delete.and.returnValue(of(false));
|
||||
});
|
||||
|
||||
it('should notify success', () => {
|
||||
component.deleteSupervisionOrder(supervisionOrderEntryMock);
|
||||
expect((component as any).modalService.open).toHaveBeenCalledWith(ConfirmationModalComponent);
|
||||
expect(notificationService.error).toHaveBeenCalled();
|
||||
expect(component.delete.emit).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('openSupervisionModal', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
spyOn(component.create, 'emit');
|
||||
spyOn((component as any).modalService, 'open').and.returnValue({
|
||||
componentInstance: { create: of(true) }
|
||||
});
|
||||
});
|
||||
|
||||
it('should emit create event properly', () => {
|
||||
component.openSupervisionModal();
|
||||
expect((component as any).modalService.open).toHaveBeenCalledWith(SupervisionOrderGroupSelectorComponent, {
|
||||
size: 'lg',
|
||||
backdrop: 'static'
|
||||
});
|
||||
expect(component.create.emit).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
});
|
@@ -0,0 +1,192 @@
|
||||
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
|
||||
|
||||
import { map, Observable } from 'rxjs';
|
||||
import { switchMap, take, tap } from 'rxjs/operators';
|
||||
import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
|
||||
import { Item } from '../../../../../core/shared/item.model';
|
||||
import { getFirstSucceededRemoteDataPayload } from '../../../../../core/shared/operators';
|
||||
import {
|
||||
SupervisionOrderGroupSelectorComponent
|
||||
} from './supervision-order-group-selector/supervision-order-group-selector.component';
|
||||
import {
|
||||
getWorkflowItemDeleteRoute
|
||||
} from '../../../../../workflowitems-edit-page/workflowitems-edit-page-routing-paths';
|
||||
import { ITEM_EDIT_AUTHORIZATIONS_PATH } from '../../../../../item-page/edit-item-page/edit-item-page.routing-paths';
|
||||
import { WorkspaceItem } from '../../../../../core/submission/models/workspaceitem.model';
|
||||
import { SupervisionOrder } from '../../../../../core/supervision-order/models/supervision-order.model';
|
||||
import { SupervisionOrderListEntry } from './supervision-order-status/supervision-order-status.component';
|
||||
import { ConfirmationModalComponent } from '../../../../../shared/confirmation-modal/confirmation-modal.component';
|
||||
import { hasValue } from '../../../../../shared/empty.util';
|
||||
import { NotificationsService } from '../../../../../shared/notifications/notifications.service';
|
||||
import { SupervisionOrderDataService } from '../../../../../core/supervision-order/supervision-order-data.service';
|
||||
import { DSONameService } from '../../../../../core/breadcrumbs/dso-name.service';
|
||||
import { DSpaceObject } from '../../../../../core/shared/dspace-object.model';
|
||||
import { getSearchResultFor } from '../../../../../shared/search/search-result-element-decorator';
|
||||
|
||||
@Component({
|
||||
selector: 'ds-workspace-item-admin-workflow-actions-element',
|
||||
styleUrls: ['./workspace-item-admin-workflow-actions.component.scss'],
|
||||
templateUrl: './workspace-item-admin-workflow-actions.component.html'
|
||||
})
|
||||
/**
|
||||
* The component for displaying the actions for a list element for a workspace-item on the admin workflow search page
|
||||
*/
|
||||
export class WorkspaceItemAdminWorkflowActionsComponent implements OnInit {
|
||||
|
||||
/**
|
||||
* The workspace item to perform the actions on
|
||||
*/
|
||||
@Input() public wsi: WorkspaceItem;
|
||||
|
||||
/**
|
||||
* Whether to use small buttons or not
|
||||
*/
|
||||
@Input() public small: boolean;
|
||||
|
||||
/**
|
||||
* The list of supervision order object to show
|
||||
*/
|
||||
@Input() supervisionOrderList: SupervisionOrder[] = [];
|
||||
|
||||
/**
|
||||
* The item related to the workspace item
|
||||
*/
|
||||
item: Item;
|
||||
|
||||
/**
|
||||
* An array containing the route to the resource policies page
|
||||
*/
|
||||
resourcePoliciesPageRoute: string[];
|
||||
|
||||
/**
|
||||
* The i18n keys prefix
|
||||
* @private
|
||||
*/
|
||||
private messagePrefix = 'workflow-item.search.result';
|
||||
|
||||
/**
|
||||
* Event emitted when a new SupervisionOrder has been created
|
||||
*/
|
||||
@Output() create: EventEmitter<DSpaceObject> = new EventEmitter<DSpaceObject>();
|
||||
|
||||
/**
|
||||
* Event emitted when new SupervisionOrder has been deleted
|
||||
*/
|
||||
@Output() delete: EventEmitter<DSpaceObject> = new EventEmitter<DSpaceObject>();
|
||||
|
||||
/**
|
||||
* Event emitted when a new SupervisionOrder has been created
|
||||
*/
|
||||
constructor(
|
||||
protected dsoNameService: DSONameService,
|
||||
protected modalService: NgbModal,
|
||||
protected notificationsService: NotificationsService,
|
||||
protected supervisionOrderDataService: SupervisionOrderDataService,
|
||||
protected translateService: TranslateService,
|
||||
) {
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
const item$: Observable<Item> = this.wsi.item.pipe(
|
||||
getFirstSucceededRemoteDataPayload(),
|
||||
);
|
||||
|
||||
item$.pipe(
|
||||
map((item: Item) => this.getPoliciesRoute(item))
|
||||
).subscribe((route: string[]) => {
|
||||
this.resourcePoliciesPageRoute = route;
|
||||
});
|
||||
|
||||
item$.subscribe((item: Item) => {
|
||||
this.item = item;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the path to the delete page of this workflow item
|
||||
*/
|
||||
getDeleteRoute(): string {
|
||||
return getWorkflowItemDeleteRoute(this.wsi.id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the path to the administrative edit page policies tab
|
||||
*/
|
||||
getPoliciesRoute(item: Item): string[] {
|
||||
return ['/items', item.uuid, 'edit', ITEM_EDIT_AUTHORIZATIONS_PATH];
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the Group from the Repository. The Group will be the only that this form is showing.
|
||||
* It'll either show a success or error message depending on whether delete was successful or not.
|
||||
*/
|
||||
deleteSupervisionOrder(supervisionOrderEntry: SupervisionOrderListEntry) {
|
||||
const modalRef = this.modalService.open(ConfirmationModalComponent);
|
||||
modalRef.componentInstance.dso = supervisionOrderEntry.group;
|
||||
modalRef.componentInstance.headerLabel = this.messagePrefix + '.delete-supervision.modal.header';
|
||||
modalRef.componentInstance.infoLabel = this.messagePrefix + '.delete-supervision.modal.info';
|
||||
modalRef.componentInstance.cancelLabel = this.messagePrefix + '.delete-supervision.modal.cancel';
|
||||
modalRef.componentInstance.confirmLabel = this.messagePrefix + '.delete-supervision.modal.confirm';
|
||||
modalRef.componentInstance.brandColor = 'danger';
|
||||
modalRef.componentInstance.confirmIcon = 'fas fa-trash';
|
||||
modalRef.componentInstance.response.pipe(
|
||||
take(1),
|
||||
switchMap((confirm: boolean) => {
|
||||
if (confirm && hasValue(supervisionOrderEntry.supervisionOrder.id)) {
|
||||
return this.supervisionOrderDataService.delete(supervisionOrderEntry.supervisionOrder.id).pipe(
|
||||
take(1),
|
||||
tap((result: boolean) => {
|
||||
if (result) {
|
||||
this.notificationsService.success(
|
||||
null,
|
||||
this.translateService.get(
|
||||
this.messagePrefix + '.notification.deleted.success',
|
||||
{ name: this.dsoNameService.getName(supervisionOrderEntry.group) }
|
||||
)
|
||||
);
|
||||
} else {
|
||||
this.notificationsService.error(
|
||||
null,
|
||||
this.translateService.get(
|
||||
this.messagePrefix + '.notification.deleted.failure',
|
||||
{ name: this.dsoNameService.getName(supervisionOrderEntry.group) }
|
||||
)
|
||||
);
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
})
|
||||
).subscribe((result: boolean) => {
|
||||
if (result) {
|
||||
this.delete.emit(this.convertReloadedObject());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens the Supervision Modal to create a supervision order
|
||||
*/
|
||||
openSupervisionModal() {
|
||||
const supervisionModal: NgbModalRef = this.modalService.open(SupervisionOrderGroupSelectorComponent, {
|
||||
size: 'lg',
|
||||
backdrop: 'static'
|
||||
});
|
||||
supervisionModal.componentInstance.itemUUID = this.item.uuid;
|
||||
supervisionModal.componentInstance.create.subscribe(() => {
|
||||
this.create.emit(this.convertReloadedObject());
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the reloadedObject to the Type required by this dso.
|
||||
*/
|
||||
private convertReloadedObject(): DSpaceObject {
|
||||
const constructor = getSearchResultFor((this.wsi as any).constructor);
|
||||
return Object.assign(new constructor(), this.wsi, {
|
||||
indexableObject: this.wsi
|
||||
});
|
||||
}
|
||||
}
|
@@ -7,14 +7,22 @@ import { TruncatableService } from '../../../../../shared/truncatable/truncatabl
|
||||
import { CollectionElementLinkType } from '../../../../../shared/object-collection/collection-element-link.type';
|
||||
import { ViewMode } from '../../../../../core/shared/view-mode.model';
|
||||
import { RouterTestingModule } from '@angular/router/testing';
|
||||
import { WorkflowItemSearchResultAdminWorkflowGridElementComponent } from './workflow-item-search-result-admin-workflow-grid-element.component';
|
||||
import {
|
||||
WorkflowItemSearchResultAdminWorkflowGridElementComponent
|
||||
} from './workflow-item-search-result-admin-workflow-grid-element.component';
|
||||
import { WorkflowItem } from '../../../../../core/submission/models/workflowitem.model';
|
||||
import { LinkService } from '../../../../../core/cache/builders/link.service';
|
||||
import { followLink } from '../../../../../shared/utils/follow-link-config.model';
|
||||
import { Item } from '../../../../../core/shared/item.model';
|
||||
import { ItemGridElementComponent } from '../../../../../shared/object-grid/item-grid-element/item-types/item/item-grid-element.component';
|
||||
import { ListableObjectDirective } from '../../../../../shared/object-collection/shared/listable-object/listable-object.directive';
|
||||
import { WorkflowItemSearchResult } from '../../../../../shared/object-collection/shared/workflow-item-search-result.model';
|
||||
import {
|
||||
ItemGridElementComponent
|
||||
} from '../../../../../shared/object-grid/item-grid-element/item-types/item/item-grid-element.component';
|
||||
import {
|
||||
ListableObjectDirective
|
||||
} from '../../../../../shared/object-collection/shared/listable-object/listable-object.directive';
|
||||
import {
|
||||
WorkflowItemSearchResult
|
||||
} from '../../../../../shared/object-collection/shared/workflow-item-search-result.model';
|
||||
import { BitstreamDataService } from '../../../../../core/data/bitstream-data.service';
|
||||
import { createSuccessfulRemoteDataObject$ } from '../../../../../shared/remote-data.utils';
|
||||
import { getMockLinkService } from '../../../../../shared/mocks/link-service.mock';
|
||||
@@ -22,7 +30,7 @@ import { of as observableOf } from 'rxjs';
|
||||
import { getMockThemeService } from '../../../../../shared/mocks/theme-service.mock';
|
||||
import { ThemeService } from '../../../../../shared/theme-support/theme.service';
|
||||
|
||||
describe('WorkflowItemAdminWorkflowGridElementComponent', () => {
|
||||
describe('WorkflowItemSearchResultAdminWorkflowGridElementComponent', () => {
|
||||
let component: WorkflowItemSearchResultAdminWorkflowGridElementComponent;
|
||||
let fixture: ComponentFixture<WorkflowItemSearchResultAdminWorkflowGridElementComponent>;
|
||||
let id;
|
||||
|
@@ -0,0 +1,16 @@
|
||||
<ng-template dsListableObject>
|
||||
</ng-template>
|
||||
<div #badges class="position-absolute ml-1">
|
||||
<div class="workflow-badge">
|
||||
<span class="badge badge-info">{{ "admin.workflow.item.workspace" | translate }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<ul #buttons class="list-group list-group-flush">
|
||||
<li class="list-group-item">
|
||||
<ds-workspace-item-admin-workflow-actions-element [small]="true"
|
||||
[supervisionOrderList]="supervisionOrder$ | async"
|
||||
[wsi]="dso"
|
||||
(create)="reloadObject($event)"
|
||||
(delete)="reloadObject($event)"></ds-workspace-item-admin-workflow-actions-element>
|
||||
</li>
|
||||
</ul>
|
@@ -0,0 +1,127 @@
|
||||
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
||||
import { RouterTestingModule } from '@angular/router/testing';
|
||||
import { NO_ERRORS_SCHEMA } from '@angular/core';
|
||||
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
|
||||
|
||||
import { of as observableOf } from 'rxjs';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
|
||||
import { TruncatableService } from '../../../../../shared/truncatable/truncatable.service';
|
||||
import { CollectionElementLinkType } from '../../../../../shared/object-collection/collection-element-link.type';
|
||||
import { ViewMode } from '../../../../../core/shared/view-mode.model';
|
||||
import {
|
||||
WorkspaceItemSearchResultAdminWorkflowGridElementComponent
|
||||
} from './workspace-item-search-result-admin-workflow-grid-element.component';
|
||||
import { WorkflowItem } from '../../../../../core/submission/models/workflowitem.model';
|
||||
import { LinkService } from '../../../../../core/cache/builders/link.service';
|
||||
import { followLink } from '../../../../../shared/utils/follow-link-config.model';
|
||||
import { Item } from '../../../../../core/shared/item.model';
|
||||
import {
|
||||
ItemGridElementComponent
|
||||
} from '../../../../../shared/object-grid/item-grid-element/item-types/item/item-grid-element.component';
|
||||
import {
|
||||
ListableObjectDirective
|
||||
} from '../../../../../shared/object-collection/shared/listable-object/listable-object.directive';
|
||||
import {
|
||||
WorkflowItemSearchResult
|
||||
} from '../../../../../shared/object-collection/shared/workflow-item-search-result.model';
|
||||
import { BitstreamDataService } from '../../../../../core/data/bitstream-data.service';
|
||||
import { createSuccessfulRemoteDataObject$ } from '../../../../../shared/remote-data.utils';
|
||||
import { getMockLinkService } from '../../../../../shared/mocks/link-service.mock';
|
||||
import { getMockThemeService } from '../../../../../shared/mocks/theme-service.mock';
|
||||
import { ThemeService } from '../../../../../shared/theme-support/theme.service';
|
||||
import {
|
||||
supervisionOrderPaginatedListRD,
|
||||
supervisionOrderPaginatedListRD$
|
||||
} from '../../../../../shared/testing/supervision-order.mock';
|
||||
import { SupervisionOrderDataService } from '../../../../../core/supervision-order/supervision-order-data.service';
|
||||
import { DSpaceObject } from '../../../../../core/shared/dspace-object.model';
|
||||
|
||||
describe('WorkspaceItemSearchResultAdminWorkflowGridElementComponent', () => {
|
||||
let component: WorkspaceItemSearchResultAdminWorkflowGridElementComponent;
|
||||
let fixture: ComponentFixture<WorkspaceItemSearchResultAdminWorkflowGridElementComponent>;
|
||||
let id;
|
||||
let wfi;
|
||||
let itemRD$;
|
||||
let linkService;
|
||||
let object;
|
||||
let themeService;
|
||||
let supervisionOrderDataService;
|
||||
|
||||
function init() {
|
||||
itemRD$ = createSuccessfulRemoteDataObject$(new Item());
|
||||
id = '780b2588-bda5-4112-a1cd-0b15000a5339';
|
||||
object = new WorkflowItemSearchResult();
|
||||
wfi = new WorkflowItem();
|
||||
wfi.item = itemRD$;
|
||||
object.indexableObject = wfi;
|
||||
linkService = getMockLinkService();
|
||||
themeService = getMockThemeService();
|
||||
supervisionOrderDataService = jasmine.createSpyObj('supervisionOrderDataService', {
|
||||
searchByItem: jasmine.createSpy('searchByItem'),
|
||||
delete: jasmine.createSpy('delete'),
|
||||
});
|
||||
}
|
||||
|
||||
beforeEach(waitForAsync(() => {
|
||||
init();
|
||||
TestBed.configureTestingModule(
|
||||
{
|
||||
declarations: [WorkspaceItemSearchResultAdminWorkflowGridElementComponent, ItemGridElementComponent, ListableObjectDirective],
|
||||
imports: [
|
||||
NoopAnimationsModule,
|
||||
TranslateModule.forRoot(),
|
||||
RouterTestingModule.withRoutes([]),
|
||||
],
|
||||
providers: [
|
||||
{ provide: LinkService, useValue: linkService },
|
||||
{ provide: ThemeService, useValue: themeService },
|
||||
{
|
||||
provide: TruncatableService, useValue: {
|
||||
isCollapsed: () => observableOf(true),
|
||||
}
|
||||
},
|
||||
{ provide: BitstreamDataService, useValue: {} },
|
||||
{ provide: SupervisionOrderDataService, useValue: supervisionOrderDataService }
|
||||
],
|
||||
schemas: [NO_ERRORS_SCHEMA]
|
||||
})
|
||||
.overrideComponent(WorkspaceItemSearchResultAdminWorkflowGridElementComponent, {
|
||||
set: {
|
||||
entryComponents: [ItemGridElementComponent]
|
||||
}
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
linkService.resolveLink.and.callFake((a) => a);
|
||||
fixture = TestBed.createComponent(WorkspaceItemSearchResultAdminWorkflowGridElementComponent);
|
||||
component = fixture.componentInstance;
|
||||
component.object = object;
|
||||
component.linkTypes = CollectionElementLinkType;
|
||||
component.index = 0;
|
||||
component.viewModes = ViewMode;
|
||||
supervisionOrderDataService.searchByItem.and.returnValue(supervisionOrderPaginatedListRD$);
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should retrieve the item using the link service', () => {
|
||||
expect(linkService.resolveLink).toHaveBeenCalledWith(wfi, followLink('item'));
|
||||
});
|
||||
|
||||
it('should retrieve supervision order objects properly', () => {
|
||||
expect(component.supervisionOrder$.value).toEqual(supervisionOrderPaginatedListRD.payload.page);
|
||||
});
|
||||
|
||||
it('should emit reloadedObject properly ', () => {
|
||||
spyOn(component.reloadedObject, 'emit');
|
||||
const dso = new DSpaceObject();
|
||||
component.reloadObject(dso);
|
||||
expect(component.reloadedObject.emit).toHaveBeenCalledWith(dso);
|
||||
});
|
||||
});
|
@@ -0,0 +1,158 @@
|
||||
import { Component, ComponentFactoryResolver, ElementRef, ViewChild } from '@angular/core';
|
||||
|
||||
import { BehaviorSubject, Observable } from 'rxjs';
|
||||
import { map, mergeMap, take, tap } from 'rxjs/operators';
|
||||
|
||||
import { Item } from '../../../../../core/shared/item.model';
|
||||
import { ViewMode } from '../../../../../core/shared/view-mode.model';
|
||||
import {
|
||||
getListableObjectComponent,
|
||||
listableObjectComponent
|
||||
} from '../../../../../shared/object-collection/shared/listable-object/listable-object.decorator';
|
||||
import { Context } from '../../../../../core/shared/context.model';
|
||||
import {
|
||||
SearchResultGridElementComponent
|
||||
} from '../../../../../shared/object-grid/search-result-grid-element/search-result-grid-element.component';
|
||||
import { TruncatableService } from '../../../../../shared/truncatable/truncatable.service';
|
||||
import { BitstreamDataService } from '../../../../../core/data/bitstream-data.service';
|
||||
import { GenericConstructor } from '../../../../../core/shared/generic-constructor';
|
||||
import {
|
||||
ListableObjectDirective
|
||||
} from '../../../../../shared/object-collection/shared/listable-object/listable-object.directive';
|
||||
import { WorkspaceItem } from '../../../../../core/submission/models/workspaceitem.model';
|
||||
import { LinkService } from '../../../../../core/cache/builders/link.service';
|
||||
import { followLink } from '../../../../../shared/utils/follow-link-config.model';
|
||||
import { RemoteData } from '../../../../../core/data/remote-data';
|
||||
import {
|
||||
getAllSucceededRemoteData,
|
||||
getFirstCompletedRemoteData,
|
||||
getRemoteDataPayload
|
||||
} from '../../../../../core/shared/operators';
|
||||
import {
|
||||
WorkspaceItemSearchResult
|
||||
} from '../../../../../shared/object-collection/shared/workspace-item-search-result.model';
|
||||
import { ThemeService } from '../../../../../shared/theme-support/theme.service';
|
||||
import { DSpaceObject } from '../../../../../core/shared/dspace-object.model';
|
||||
import { SupervisionOrder } from '../../../../../core/supervision-order/models/supervision-order.model';
|
||||
import { PaginatedList } from '../../../../../core/data/paginated-list.model';
|
||||
import { SupervisionOrderDataService } from '../../../../../core/supervision-order/supervision-order-data.service';
|
||||
|
||||
@listableObjectComponent(WorkspaceItemSearchResult, ViewMode.GridElement, Context.AdminWorkflowSearch)
|
||||
@Component({
|
||||
selector: 'ds-workflow-item-search-result-admin-workflow-grid-element',
|
||||
styleUrls: ['./workspace-item-search-result-admin-workflow-grid-element.component.scss'],
|
||||
templateUrl: './workspace-item-search-result-admin-workflow-grid-element.component.html'
|
||||
})
|
||||
/**
|
||||
* The component for displaying a grid element for an workflow item on the admin workflow search page
|
||||
*/
|
||||
export class WorkspaceItemSearchResultAdminWorkflowGridElementComponent extends SearchResultGridElementComponent<WorkspaceItemSearchResult, WorkspaceItem> {
|
||||
|
||||
/**
|
||||
* The item linked to the workspace item
|
||||
*/
|
||||
public item$: Observable<Item>;
|
||||
|
||||
/**
|
||||
* The id of the item linked to the workflow item
|
||||
*/
|
||||
public itemId: string;
|
||||
|
||||
/**
|
||||
* The supervision orders linked to the workflow item
|
||||
*/
|
||||
public supervisionOrder$: BehaviorSubject<SupervisionOrder[]> = new BehaviorSubject<SupervisionOrder[]>([]);
|
||||
|
||||
/**
|
||||
* Directive used to render the dynamic component in
|
||||
*/
|
||||
@ViewChild(ListableObjectDirective, { static: true }) listableObjectDirective: ListableObjectDirective;
|
||||
|
||||
/**
|
||||
* The html child that contains the badges html
|
||||
*/
|
||||
@ViewChild('badges', { static: true }) badges: ElementRef;
|
||||
|
||||
/**
|
||||
* The html child that contains the button html
|
||||
*/
|
||||
@ViewChild('buttons', { static: true }) buttons: ElementRef;
|
||||
|
||||
constructor(
|
||||
private componentFactoryResolver: ComponentFactoryResolver,
|
||||
private linkService: LinkService,
|
||||
protected truncatableService: TruncatableService,
|
||||
private themeService: ThemeService,
|
||||
protected bitstreamDataService: BitstreamDataService,
|
||||
protected supervisionOrderDataService: SupervisionOrderDataService,
|
||||
) {
|
||||
super(truncatableService, bitstreamDataService);
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup the dynamic child component
|
||||
* Initialize the item object from the workflow item
|
||||
*/
|
||||
ngOnInit(): void {
|
||||
super.ngOnInit();
|
||||
this.dso = this.linkService.resolveLink(this.dso, followLink('item'));
|
||||
this.item$ = (this.dso.item as Observable<RemoteData<Item>>).pipe(getAllSucceededRemoteData(), getRemoteDataPayload());
|
||||
this.item$.pipe(take(1)).subscribe((item: Item) => {
|
||||
const componentFactory = this.componentFactoryResolver.resolveComponentFactory(this.getComponent(item));
|
||||
|
||||
const viewContainerRef = this.listableObjectDirective.viewContainerRef;
|
||||
viewContainerRef.clear();
|
||||
|
||||
const componentRef = viewContainerRef.createComponent(
|
||||
componentFactory,
|
||||
0,
|
||||
undefined,
|
||||
[
|
||||
[this.badges.nativeElement],
|
||||
[this.buttons.nativeElement]
|
||||
]);
|
||||
(componentRef.instance as any).object = item;
|
||||
(componentRef.instance as any).index = this.index;
|
||||
(componentRef.instance as any).linkType = this.linkType;
|
||||
(componentRef.instance as any).listID = this.listID;
|
||||
componentRef.changeDetectorRef.detectChanges();
|
||||
}
|
||||
);
|
||||
|
||||
this.item$.pipe(
|
||||
take(1),
|
||||
tap((item: Item) => this.itemId = item.id),
|
||||
mergeMap((item: Item) => this.retrieveSupervisorOrders(item.id))
|
||||
).subscribe((supervisionOrderList: SupervisionOrder[]) => {
|
||||
this.supervisionOrder$.next(supervisionOrderList);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch the component depending on the item's entity type, view mode and context
|
||||
* @returns {GenericConstructor<Component>}
|
||||
*/
|
||||
private getComponent(item: Item): GenericConstructor<Component> {
|
||||
return getListableObjectComponent(item.getRenderTypes(), ViewMode.GridElement, undefined, this.themeService.getThemeName());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieve the list of SupervisionOrder object related to the given item
|
||||
*
|
||||
* @param itemId
|
||||
* @private
|
||||
*/
|
||||
private retrieveSupervisorOrders(itemId): Observable<SupervisionOrder[]> {
|
||||
return this.supervisionOrderDataService.searchByItem(
|
||||
itemId, false, true, followLink('group')
|
||||
).pipe(
|
||||
getFirstCompletedRemoteData(),
|
||||
map((soRD: RemoteData<PaginatedList<SupervisionOrder>>) => soRD.hasSucceeded && !soRD.hasNoContent ? soRD.payload.page : [])
|
||||
);
|
||||
}
|
||||
|
||||
reloadObject(dso: DSpaceObject) {
|
||||
this.reloadedObject.emit(dso);
|
||||
}
|
||||
}
|
@@ -9,11 +9,15 @@ import { CollectionElementLinkType } from '../../../../../shared/object-collecti
|
||||
import { ViewMode } from '../../../../../core/shared/view-mode.model';
|
||||
import { RouterTestingModule } from '@angular/router/testing';
|
||||
import { WorkflowItem } from '../../../../../core/submission/models/workflowitem.model';
|
||||
import { WorkflowItemSearchResultAdminWorkflowListElementComponent } from './workflow-item-search-result-admin-workflow-list-element.component';
|
||||
import {
|
||||
WorkflowItemSearchResultAdminWorkflowListElementComponent
|
||||
} from './workflow-item-search-result-admin-workflow-list-element.component';
|
||||
import { LinkService } from '../../../../../core/cache/builders/link.service';
|
||||
import { followLink } from '../../../../../shared/utils/follow-link-config.model';
|
||||
import { Item } from '../../../../../core/shared/item.model';
|
||||
import { WorkflowItemSearchResult } from '../../../../../shared/object-collection/shared/workflow-item-search-result.model';
|
||||
import {
|
||||
WorkflowItemSearchResult
|
||||
} from '../../../../../shared/object-collection/shared/workflow-item-search-result.model';
|
||||
import { createSuccessfulRemoteDataObject$ } from '../../../../../shared/remote-data.utils';
|
||||
import { getMockLinkService } from '../../../../../shared/mocks/link-service.mock';
|
||||
import { DSONameService } from '../../../../../core/breadcrumbs/dso-name.service';
|
||||
@@ -21,7 +25,7 @@ import { DSONameServiceMock } from '../../../../../shared/mocks/dso-name.service
|
||||
import { APP_CONFIG } from '../../../../../../config/app-config.interface';
|
||||
import { environment } from '../../../../../../environments/environment';
|
||||
|
||||
describe('WorkflowItemAdminWorkflowListElementComponent', () => {
|
||||
describe('WorkflowItemSearchResultAdminWorkflowListElementComponent', () => {
|
||||
let component: WorkflowItemSearchResultAdminWorkflowListElementComponent;
|
||||
let fixture: ComponentFixture<WorkflowItemSearchResultAdminWorkflowListElementComponent>;
|
||||
let id;
|
||||
|
@@ -1,6 +1,8 @@
|
||||
import { Component, Inject, OnInit } from '@angular/core';
|
||||
import { ViewMode } from '../../../../../core/shared/view-mode.model';
|
||||
import { listableObjectComponent } from '../../../../../shared/object-collection/shared/listable-object/listable-object.decorator';
|
||||
import {
|
||||
listableObjectComponent
|
||||
} from '../../../../../shared/object-collection/shared/listable-object/listable-object.decorator';
|
||||
import { Context } from '../../../../../core/shared/context.model';
|
||||
import { WorkflowItem } from '../../../../../core/submission/models/workflowitem.model';
|
||||
import { Observable } from 'rxjs';
|
||||
@@ -9,9 +11,13 @@ import { followLink } from '../../../../../shared/utils/follow-link-config.model
|
||||
import { RemoteData } from '../../../../../core/data/remote-data';
|
||||
import { getAllSucceededRemoteData, getRemoteDataPayload } from '../../../../../core/shared/operators';
|
||||
import { Item } from '../../../../../core/shared/item.model';
|
||||
import { SearchResultListElementComponent } from '../../../../../shared/object-list/search-result-list-element/search-result-list-element.component';
|
||||
import {
|
||||
SearchResultListElementComponent
|
||||
} from '../../../../../shared/object-list/search-result-list-element/search-result-list-element.component';
|
||||
import { TruncatableService } from '../../../../../shared/truncatable/truncatable.service';
|
||||
import { WorkflowItemSearchResult } from '../../../../../shared/object-collection/shared/workflow-item-search-result.model';
|
||||
import {
|
||||
WorkflowItemSearchResult
|
||||
} from '../../../../../shared/object-collection/shared/workflow-item-search-result.model';
|
||||
import { DSONameService } from '../../../../../core/breadcrumbs/dso-name.service';
|
||||
import { APP_CONFIG, AppConfig } from '../../../../../../config/app-config.interface';
|
||||
|
||||
@@ -22,7 +28,7 @@ import { APP_CONFIG, AppConfig } from '../../../../../../config/app-config.inter
|
||||
templateUrl: './workflow-item-search-result-admin-workflow-list-element.component.html'
|
||||
})
|
||||
/**
|
||||
* The component for displaying a list element for an workflow item on the admin workflow search page
|
||||
* The component for displaying a list element for a workflow item on the admin workflow search page
|
||||
*/
|
||||
export class WorkflowItemSearchResultAdminWorkflowListElementComponent extends SearchResultListElementComponent<WorkflowItemSearchResult, WorkflowItem> implements OnInit {
|
||||
|
||||
|
@@ -0,0 +1,13 @@
|
||||
<span class="badge badge-info">{{ "admin.workflow.item.workspace" | translate }}</span>
|
||||
<ds-listable-object-component-loader *ngIf="item$ | async"
|
||||
[object]="item$ | async"
|
||||
[viewMode]="viewModes.ListElement"
|
||||
[index]="index"
|
||||
[linkType]="linkType"
|
||||
[listID]="listID"></ds-listable-object-component-loader>
|
||||
|
||||
<ds-workspace-item-admin-workflow-actions-element [small]="false"
|
||||
[supervisionOrderList]="supervisionOrder$ | async"
|
||||
[wsi]="dso"
|
||||
(create)="reloadObject($event)"
|
||||
(delete)="reloadObject($event)"></ds-workspace-item-admin-workflow-actions-element>
|
@@ -0,0 +1,111 @@
|
||||
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
||||
import { RouterTestingModule } from '@angular/router/testing';
|
||||
import { NO_ERRORS_SCHEMA } from '@angular/core';
|
||||
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
|
||||
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
|
||||
import { mockTruncatableService } from '../../../../../shared/mocks/mock-trucatable.service';
|
||||
import { TruncatableService } from '../../../../../shared/truncatable/truncatable.service';
|
||||
import { CollectionElementLinkType } from '../../../../../shared/object-collection/collection-element-link.type';
|
||||
import { ViewMode } from '../../../../../core/shared/view-mode.model';
|
||||
import { WorkflowItem } from '../../../../../core/submission/models/workflowitem.model';
|
||||
import {
|
||||
WorkspaceItemSearchResultAdminWorkflowListElementComponent
|
||||
} from './workspace-item-search-result-admin-workflow-list-element.component';
|
||||
import { LinkService } from '../../../../../core/cache/builders/link.service';
|
||||
import { followLink } from '../../../../../shared/utils/follow-link-config.model';
|
||||
import { Item } from '../../../../../core/shared/item.model';
|
||||
import {
|
||||
WorkflowItemSearchResult
|
||||
} from '../../../../../shared/object-collection/shared/workflow-item-search-result.model';
|
||||
import { createSuccessfulRemoteDataObject$ } from '../../../../../shared/remote-data.utils';
|
||||
import { getMockLinkService } from '../../../../../shared/mocks/link-service.mock';
|
||||
import { DSONameService } from '../../../../../core/breadcrumbs/dso-name.service';
|
||||
import { DSONameServiceMock } from '../../../../../shared/mocks/dso-name.service.mock';
|
||||
import { APP_CONFIG } from '../../../../../../config/app-config.interface';
|
||||
import { environment } from '../../../../../../environments/environment';
|
||||
import { SupervisionOrderDataService } from '../../../../../core/supervision-order/supervision-order-data.service';
|
||||
import {
|
||||
supervisionOrderPaginatedListRD,
|
||||
supervisionOrderPaginatedListRD$
|
||||
} from '../../../../../shared/testing/supervision-order.mock';
|
||||
import { DSpaceObject } from '../../../../../core/shared/dspace-object.model';
|
||||
|
||||
describe('WorkspaceItemSearchResultAdminWorkflowListElementComponent', () => {
|
||||
let component: WorkspaceItemSearchResultAdminWorkflowListElementComponent;
|
||||
let fixture: ComponentFixture<WorkspaceItemSearchResultAdminWorkflowListElementComponent>;
|
||||
let id;
|
||||
let wfi;
|
||||
let itemRD$;
|
||||
let linkService;
|
||||
let object;
|
||||
let supervisionOrderDataService;
|
||||
|
||||
function init() {
|
||||
itemRD$ = createSuccessfulRemoteDataObject$(new Item());
|
||||
id = '780b2588-bda5-4112-a1cd-0b15000a5339';
|
||||
object = new WorkflowItemSearchResult();
|
||||
wfi = new WorkflowItem();
|
||||
wfi.item = itemRD$;
|
||||
object.indexableObject = wfi;
|
||||
linkService = getMockLinkService();
|
||||
supervisionOrderDataService = jasmine.createSpyObj('supervisionOrderDataService', {
|
||||
searchByItem: jasmine.createSpy('searchByItem'),
|
||||
delete: jasmine.createSpy('delete'),
|
||||
});
|
||||
}
|
||||
|
||||
beforeEach(waitForAsync(() => {
|
||||
init();
|
||||
TestBed.configureTestingModule(
|
||||
{
|
||||
declarations: [WorkspaceItemSearchResultAdminWorkflowListElementComponent],
|
||||
imports: [
|
||||
NoopAnimationsModule,
|
||||
TranslateModule.forRoot(),
|
||||
RouterTestingModule.withRoutes([]),
|
||||
],
|
||||
providers: [
|
||||
{ provide: TruncatableService, useValue: mockTruncatableService },
|
||||
{ provide: LinkService, useValue: linkService },
|
||||
{ provide: DSONameService, useClass: DSONameServiceMock },
|
||||
{ provide: SupervisionOrderDataService, useValue: supervisionOrderDataService },
|
||||
{ provide: APP_CONFIG, useValue: environment }
|
||||
],
|
||||
schemas: [NO_ERRORS_SCHEMA]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
linkService.resolveLink.and.callFake((a) => a);
|
||||
fixture = TestBed.createComponent(WorkspaceItemSearchResultAdminWorkflowListElementComponent);
|
||||
component = fixture.componentInstance;
|
||||
component.object = object;
|
||||
component.linkTypes = CollectionElementLinkType;
|
||||
component.index = 0;
|
||||
component.viewModes = ViewMode;
|
||||
supervisionOrderDataService.searchByItem.and.returnValue(supervisionOrderPaginatedListRD$);
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should retrieve the item using the link service', () => {
|
||||
expect(linkService.resolveLink).toHaveBeenCalledWith(wfi, followLink('item'));
|
||||
});
|
||||
|
||||
it('should retrieve supervision order objects properly', () => {
|
||||
expect(component.supervisionOrder$.value).toEqual(supervisionOrderPaginatedListRD.payload.page);
|
||||
});
|
||||
|
||||
it('should emit reloadedObject properly ', () => {
|
||||
spyOn(component.reloadedObject, 'emit');
|
||||
const dso = new DSpaceObject();
|
||||
component.reloadObject(dso);
|
||||
expect(component.reloadedObject.emit).toHaveBeenCalledWith(dso);
|
||||
});
|
||||
});
|
@@ -0,0 +1,109 @@
|
||||
import { Component, Inject, OnInit } from '@angular/core';
|
||||
|
||||
import { BehaviorSubject, Observable } from 'rxjs';
|
||||
import { map, mergeMap, take, tap } from 'rxjs/operators';
|
||||
|
||||
import { ViewMode } from '../../../../../core/shared/view-mode.model';
|
||||
import {
|
||||
listableObjectComponent
|
||||
} from '../../../../../shared/object-collection/shared/listable-object/listable-object.decorator';
|
||||
import { Context } from '../../../../../core/shared/context.model';
|
||||
import { WorkspaceItem } from '../../../../../core/submission/models/workspaceitem.model';
|
||||
import { LinkService } from '../../../../../core/cache/builders/link.service';
|
||||
import { followLink } from '../../../../../shared/utils/follow-link-config.model';
|
||||
import { RemoteData } from '../../../../../core/data/remote-data';
|
||||
import {
|
||||
getAllSucceededRemoteData,
|
||||
getFirstCompletedRemoteData,
|
||||
getRemoteDataPayload
|
||||
} from '../../../../../core/shared/operators';
|
||||
import { Item } from '../../../../../core/shared/item.model';
|
||||
import {
|
||||
SearchResultListElementComponent
|
||||
} from '../../../../../shared/object-list/search-result-list-element/search-result-list-element.component';
|
||||
import { TruncatableService } from '../../../../../shared/truncatable/truncatable.service';
|
||||
import { DSONameService } from '../../../../../core/breadcrumbs/dso-name.service';
|
||||
import { APP_CONFIG, AppConfig } from '../../../../../../config/app-config.interface';
|
||||
import {
|
||||
WorkspaceItemSearchResult
|
||||
} from '../../../../../shared/object-collection/shared/workspace-item-search-result.model';
|
||||
import { SupervisionOrder } from '../../../../../core/supervision-order/models/supervision-order.model';
|
||||
import { SupervisionOrderDataService } from '../../../../../core/supervision-order/supervision-order-data.service';
|
||||
import { PaginatedList } from '../../../../../core/data/paginated-list.model';
|
||||
import { DSpaceObject } from '../../../../../core/shared/dspace-object.model';
|
||||
|
||||
@listableObjectComponent(WorkspaceItemSearchResult, ViewMode.ListElement, Context.AdminWorkflowSearch)
|
||||
@Component({
|
||||
selector: 'ds-workflow-item-search-result-admin-workflow-list-element',
|
||||
styleUrls: ['./workspace-item-search-result-admin-workflow-list-element.component.scss'],
|
||||
templateUrl: './workspace-item-search-result-admin-workflow-list-element.component.html'
|
||||
})
|
||||
/**
|
||||
* The component for displaying a list element for a workflow item on the admin workflow search page
|
||||
*/
|
||||
export class WorkspaceItemSearchResultAdminWorkflowListElementComponent extends SearchResultListElementComponent<WorkspaceItemSearchResult, WorkspaceItem> implements OnInit {
|
||||
|
||||
/**
|
||||
* The item linked to the workflow item
|
||||
*/
|
||||
public item$: Observable<Item>;
|
||||
|
||||
/**
|
||||
* The id of the item linked to the workflow item
|
||||
*/
|
||||
public itemId: string;
|
||||
|
||||
/**
|
||||
* The supervision orders linked to the workflow item
|
||||
*/
|
||||
public supervisionOrder$: BehaviorSubject<SupervisionOrder[]> = new BehaviorSubject<SupervisionOrder[]>([]);
|
||||
|
||||
constructor(private linkService: LinkService,
|
||||
protected dsoNameService: DSONameService,
|
||||
protected supervisionOrderDataService: SupervisionOrderDataService,
|
||||
protected truncatableService: TruncatableService,
|
||||
@Inject(APP_CONFIG) protected appConfig: AppConfig
|
||||
) {
|
||||
super(truncatableService, dsoNameService, appConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the item object from the workflow item
|
||||
*/
|
||||
ngOnInit(): void {
|
||||
super.ngOnInit();
|
||||
this.dso = this.linkService.resolveLink(this.dso, followLink('item'));
|
||||
this.item$ = (this.dso.item as Observable<RemoteData<Item>>).pipe(getAllSucceededRemoteData(), getRemoteDataPayload());
|
||||
|
||||
this.item$.pipe(
|
||||
take(1),
|
||||
tap((item: Item) => this.itemId = item.id),
|
||||
mergeMap((item: Item) => this.retrieveSupervisorOrders(item.id))
|
||||
).subscribe((supervisionOrderList: SupervisionOrder[]) => {
|
||||
this.supervisionOrder$.next(supervisionOrderList);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the list of SupervisionOrder object related to the given item
|
||||
*
|
||||
* @param itemId
|
||||
* @private
|
||||
*/
|
||||
private retrieveSupervisorOrders(itemId): Observable<SupervisionOrder[]> {
|
||||
return this.supervisionOrderDataService.searchByItem(
|
||||
itemId, false, true, followLink('group')
|
||||
).pipe(
|
||||
getFirstCompletedRemoteData(),
|
||||
map((soRD: RemoteData<PaginatedList<SupervisionOrder>>) => soRD.hasSucceeded && !soRD.hasNoContent ? soRD.payload.page : [])
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reload list element after supervision order change.
|
||||
*/
|
||||
reloadObject(dso: DSpaceObject) {
|
||||
this.reloadedObject.emit(dso);
|
||||
}
|
||||
|
||||
}
|
@@ -1,16 +1,39 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { SharedModule } from '../../shared/shared.module';
|
||||
|
||||
import { WorkflowItemSearchResultAdminWorkflowGridElementComponent } from './admin-workflow-search-results/admin-workflow-search-result-grid-element/workflow-item/workflow-item-search-result-admin-workflow-grid-element.component';
|
||||
import { WorkflowItemAdminWorkflowActionsComponent } from './admin-workflow-search-results/workflow-item-admin-workflow-actions.component';
|
||||
import { WorkflowItemSearchResultAdminWorkflowListElementComponent } from './admin-workflow-search-results/admin-workflow-search-result-list-element/workflow-item/workflow-item-search-result-admin-workflow-list-element.component';
|
||||
import { SharedModule } from '../../shared/shared.module';
|
||||
import {
|
||||
WorkflowItemSearchResultAdminWorkflowGridElementComponent
|
||||
} from './admin-workflow-search-results/admin-workflow-search-result-grid-element/workflow-item/workflow-item-search-result-admin-workflow-grid-element.component';
|
||||
import {
|
||||
WorkflowItemAdminWorkflowActionsComponent
|
||||
} from './admin-workflow-search-results/actions/workflow-item/workflow-item-admin-workflow-actions.component';
|
||||
import {
|
||||
WorkflowItemSearchResultAdminWorkflowListElementComponent
|
||||
} from './admin-workflow-search-results/admin-workflow-search-result-list-element/workflow-item/workflow-item-search-result-admin-workflow-list-element.component';
|
||||
import { AdminWorkflowPageComponent } from './admin-workflow-page.component';
|
||||
import { SearchModule } from '../../shared/search/search.module';
|
||||
import {
|
||||
WorkspaceItemAdminWorkflowActionsComponent
|
||||
} from './admin-workflow-search-results/actions/workspace-item/workspace-item-admin-workflow-actions.component';
|
||||
import {
|
||||
WorkspaceItemSearchResultAdminWorkflowListElementComponent
|
||||
} from './admin-workflow-search-results/admin-workflow-search-result-list-element/workspace-item/workspace-item-search-result-admin-workflow-list-element.component';
|
||||
import {
|
||||
WorkspaceItemSearchResultAdminWorkflowGridElementComponent
|
||||
} from './admin-workflow-search-results/admin-workflow-search-result-grid-element/workspace-item/workspace-item-search-result-admin-workflow-grid-element.component';
|
||||
import {
|
||||
SupervisionOrderGroupSelectorComponent
|
||||
} from './admin-workflow-search-results/actions/workspace-item/supervision-order-group-selector/supervision-order-group-selector.component';
|
||||
import {
|
||||
SupervisionOrderStatusComponent
|
||||
} from './admin-workflow-search-results/actions/workspace-item/supervision-order-status/supervision-order-status.component';
|
||||
|
||||
const ENTRY_COMPONENTS = [
|
||||
// put only entry components that use custom decorator
|
||||
WorkflowItemSearchResultAdminWorkflowListElementComponent,
|
||||
WorkflowItemSearchResultAdminWorkflowGridElementComponent,
|
||||
WorkspaceItemSearchResultAdminWorkflowListElementComponent,
|
||||
WorkspaceItemSearchResultAdminWorkflowGridElementComponent
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
@@ -20,7 +43,10 @@ const ENTRY_COMPONENTS = [
|
||||
],
|
||||
declarations: [
|
||||
AdminWorkflowPageComponent,
|
||||
SupervisionOrderGroupSelectorComponent,
|
||||
SupervisionOrderStatusComponent,
|
||||
WorkflowItemAdminWorkflowActionsComponent,
|
||||
WorkspaceItemAdminWorkflowActionsComponent,
|
||||
...ENTRY_COMPONENTS
|
||||
],
|
||||
exports: [
|
||||
|
@@ -175,6 +175,7 @@ import { VocabularyDataService } from './submission/vocabularies/vocabulary.data
|
||||
import { VocabularyEntryDetailsDataService } from './submission/vocabularies/vocabulary-entry-details.data.service';
|
||||
import { IdentifierData } from '../shared/object-list/identifier-data/identifier-data.model';
|
||||
import { Subscription } from '../shared/subscriptions/models/subscription.model';
|
||||
import { SupervisionOrderDataService } from './supervision-order/supervision-order-data.service';
|
||||
|
||||
/**
|
||||
* When not in production, endpoint responses can be mocked for testing purposes
|
||||
@@ -297,6 +298,7 @@ const PROVIDERS = [
|
||||
OrcidAuthService,
|
||||
OrcidQueueDataService,
|
||||
OrcidHistoryDataService,
|
||||
SupervisionOrderDataService
|
||||
];
|
||||
|
||||
/**
|
||||
|
@@ -8,6 +8,7 @@ export enum Context {
|
||||
Search = 'search',
|
||||
Workflow = 'workflow',
|
||||
Workspace = 'workspace',
|
||||
SupervisedItems = 'supervisedWorkspace',
|
||||
AdminMenu = 'adminMenu',
|
||||
EntitySearchModalWithNameVariants = 'EntitySearchModalWithNameVariants',
|
||||
EntitySearchModal = 'EntitySearchModal',
|
||||
|
@@ -14,6 +14,9 @@ import { ITEM } from '../../shared/item.resource-type';
|
||||
import { excludeFromEquals } from '../../utilities/equals.decorators';
|
||||
import { WorkspaceitemSectionsObject } from './workspaceitem-sections.model';
|
||||
import { CacheableObject } from '../../cache/cacheable-object.model';
|
||||
import { SUPERVISION_ORDER } from '../../supervision-order/models/supervision-order.resource-type';
|
||||
import { PaginatedList } from '../../data/paginated-list.model';
|
||||
import { SupervisionOrder } from '../../supervision-order/models/supervision-order.model';
|
||||
|
||||
export interface SubmissionObjectError {
|
||||
message: string;
|
||||
@@ -65,6 +68,7 @@ export abstract class SubmissionObject extends DSpaceObject implements Cacheable
|
||||
item: HALLink;
|
||||
submissionDefinition: HALLink;
|
||||
submitter: HALLink;
|
||||
supervisionOrders: HALLink;
|
||||
};
|
||||
|
||||
get self(): string {
|
||||
@@ -93,4 +97,12 @@ export abstract class SubmissionObject extends DSpaceObject implements Cacheable
|
||||
@link(EPERSON)
|
||||
submitter?: Observable<RemoteData<EPerson>> | EPerson;
|
||||
|
||||
/**
|
||||
* The submission supervision order
|
||||
* Will be undefined unless the workspace item {@link HALLink} has been resolved.
|
||||
*/
|
||||
@link(SUPERVISION_ORDER)
|
||||
/* This was changed from 'Observable<RemoteData<WorkspaceItem>> | WorkspaceItem' to 'any' to prevent issues in templates with async */
|
||||
supervisionOrders?: Observable<RemoteData<PaginatedList<SupervisionOrder>>>;
|
||||
|
||||
}
|
||||
|
44
src/app/core/supervision-order/models/action-type.model.ts
Normal file
44
src/app/core/supervision-order/models/action-type.model.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
/**
|
||||
* Enum representing the Action Type of a Resource Policy
|
||||
*/
|
||||
export enum ActionType {
|
||||
/**
|
||||
* Action of reading, viewing or downloading something
|
||||
*/
|
||||
READ = 'READ',
|
||||
|
||||
/**
|
||||
* Action of modifying something
|
||||
*/
|
||||
WRITE = 'WRITE',
|
||||
|
||||
/**
|
||||
* Action of deleting something
|
||||
*/
|
||||
DELETE = 'DELETE',
|
||||
|
||||
/**
|
||||
* Action of adding something to a container
|
||||
*/
|
||||
ADD = 'ADD',
|
||||
|
||||
/**
|
||||
* Action of removing something from a container
|
||||
*/
|
||||
REMOVE = 'REMOVE',
|
||||
|
||||
/**
|
||||
* None Type of Supervision Order
|
||||
*/
|
||||
NONE = 'NONE',
|
||||
|
||||
/**
|
||||
* Editor Type of Supervision Order
|
||||
*/
|
||||
EDITOR = 'EDITOR',
|
||||
|
||||
/**
|
||||
* Observer Type of Supervision Order
|
||||
*/
|
||||
OBSERVER = 'OBSERVER',
|
||||
}
|
@@ -0,0 +1,74 @@
|
||||
import { autoserialize, deserialize, deserializeAs } from 'cerialize';
|
||||
import { link, typedObject } from '../../cache/builders/build-decorators';
|
||||
import { IDToUUIDSerializer } from '../../cache/id-to-uuid-serializer';
|
||||
import { HALLink } from '../../shared/hal-link.model';
|
||||
import { SUPERVISION_ORDER } from './supervision-order.resource-type';
|
||||
import { excludeFromEquals } from '../../utilities/equals.decorators';
|
||||
import { ResourceType } from '../../shared/resource-type';
|
||||
import { Observable } from 'rxjs';
|
||||
import { RemoteData } from '../../data/remote-data';
|
||||
import { GROUP } from '../../eperson/models/group.resource-type';
|
||||
import { Group } from '../../eperson/models/group.model';
|
||||
import { CacheableObject } from '../../cache/cacheable-object.model';
|
||||
import { ITEM } from '../../shared/item.resource-type';
|
||||
import { Item } from '../../shared/item.model';
|
||||
|
||||
/**
|
||||
* Model class for a Supervision Order
|
||||
*/
|
||||
@typedObject
|
||||
export class SupervisionOrder implements CacheableObject {
|
||||
static type = SUPERVISION_ORDER;
|
||||
|
||||
/**
|
||||
* The identifier for this Supervision Order
|
||||
*/
|
||||
@autoserialize
|
||||
id: string;
|
||||
|
||||
/**
|
||||
* The object type
|
||||
*/
|
||||
@excludeFromEquals
|
||||
@autoserialize
|
||||
type: ResourceType;
|
||||
|
||||
/**
|
||||
* The object type
|
||||
*/
|
||||
@excludeFromEquals
|
||||
@autoserialize
|
||||
ordertype: string;
|
||||
|
||||
/**
|
||||
* The universally unique identifier for this Supervision Order
|
||||
* This UUID is generated client-side and isn't used by the backend.
|
||||
* It is based on the ID, so it will be the same for each refresh.
|
||||
*/
|
||||
@deserializeAs(new IDToUUIDSerializer('supervision-order'), 'id')
|
||||
uuid: string;
|
||||
|
||||
/**
|
||||
* The {@link HALLink}s for this SupervisionOrder
|
||||
*/
|
||||
@deserialize
|
||||
_links: {
|
||||
item: HALLink,
|
||||
group: HALLink,
|
||||
self: HALLink,
|
||||
};
|
||||
|
||||
/**
|
||||
* The related supervision Item
|
||||
* Will be undefined unless the item {@link HALLink} has been resolved.
|
||||
*/
|
||||
@link(ITEM)
|
||||
item?: Observable<RemoteData<Item>>;
|
||||
|
||||
/**
|
||||
* The group linked by this supervision order
|
||||
* Will be undefined unless the version {@link HALLink} has been resolved.
|
||||
*/
|
||||
@link(GROUP)
|
||||
group?: Observable<RemoteData<Group>>;
|
||||
}
|
@@ -0,0 +1,9 @@
|
||||
import { ResourceType } from '../../shared/resource-type';
|
||||
|
||||
/**
|
||||
* The resource type for SupervisionOrder
|
||||
*
|
||||
* Needs to be in a separate file to prevent circular
|
||||
* dependencies in webpack.
|
||||
*/
|
||||
export const SUPERVISION_ORDER = new ResourceType('supervisionorder');
|
@@ -0,0 +1,277 @@
|
||||
import { cold, getTestScheduler, hot } from 'jasmine-marbles';
|
||||
import { of as observableOf } from 'rxjs';
|
||||
import { TestScheduler } from 'rxjs/testing';
|
||||
|
||||
import { NotificationsService } from '../../shared/notifications/notifications.service';
|
||||
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
|
||||
import { ObjectCacheService } from '../cache/object-cache.service';
|
||||
import { HALEndpointService } from '../shared/hal-endpoint.service';
|
||||
import { RequestService } from '../data/request.service';
|
||||
import { SupervisionOrderDataService } from './supervision-order-data.service';
|
||||
import { ActionType } from './models/action-type.model';
|
||||
import { RequestParam } from '../cache/models/request-param.model';
|
||||
import { PageInfo } from '../shared/page-info.model';
|
||||
import { buildPaginatedList } from '../data/paginated-list.model';
|
||||
import { createSuccessfulRemoteDataObject } from '../../shared/remote-data.utils';
|
||||
import { RestResponse } from '../cache/response.models';
|
||||
import { RequestEntry } from '../data/request-entry.model';
|
||||
import { FindListOptions } from '../data/find-list-options.model';
|
||||
import { GroupDataService } from '../eperson/group-data.service';
|
||||
|
||||
describe('SupervisionOrderService', () => {
|
||||
let scheduler: TestScheduler;
|
||||
let service: SupervisionOrderDataService;
|
||||
let requestService: RequestService;
|
||||
let rdbService: RemoteDataBuildService;
|
||||
let objectCache: ObjectCacheService;
|
||||
let halService: HALEndpointService;
|
||||
let responseCacheEntry: RequestEntry;
|
||||
let groupService: GroupDataService;
|
||||
|
||||
const supervisionOrder: any = {
|
||||
id: '1',
|
||||
name: null,
|
||||
description: null,
|
||||
action: ActionType.READ,
|
||||
startDate: null,
|
||||
endDate: null,
|
||||
type: 'supervisionOrder',
|
||||
uuid: 'supervision-order-1',
|
||||
_links: {
|
||||
group: {
|
||||
href: 'https://rest.api/rest/api/group'
|
||||
},
|
||||
self: {
|
||||
href: 'https://rest.api/rest/api/supervisionorder/1'
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
const anothersupervisionOrder: any = {
|
||||
id: '2',
|
||||
name: null,
|
||||
description: null,
|
||||
action: ActionType.WRITE,
|
||||
startDate: null,
|
||||
endDate: null,
|
||||
type: 'supervisionOrder',
|
||||
uuid: 'supervision-order-2',
|
||||
_links: {
|
||||
group: {
|
||||
href: 'https://rest.api/rest/api/group'
|
||||
},
|
||||
self: {
|
||||
href: 'https://rest.api/rest/api/supervisionorder/1'
|
||||
},
|
||||
}
|
||||
};
|
||||
const endpointURL = `https://rest.api/rest/api/supervisionorder`;
|
||||
const requestURL = `https://rest.api/rest/api/supervisionorder/${supervisionOrder.id}`;
|
||||
const requestUUID = '8b3c613a-5a4b-438b-9686-be1d5b4a1c5a';
|
||||
const supervisionOrderId = '1';
|
||||
const groupUUID = '8b39g7ya-5a4b-438b-9686-be1d5b4a1c5a';
|
||||
const itemUUID = '8b39g7ya-5a4b-438b-851f-be1d5b4a1c5a';
|
||||
const supervisionOrderType = 'NONE';
|
||||
|
||||
const pageInfo = new PageInfo();
|
||||
const array = [supervisionOrder, anothersupervisionOrder];
|
||||
const paginatedList = buildPaginatedList(pageInfo, array);
|
||||
const supervisionOrderRD = createSuccessfulRemoteDataObject(supervisionOrder);
|
||||
const paginatedListRD = createSuccessfulRemoteDataObject(paginatedList);
|
||||
|
||||
const groupEndpoint = 'group_EP';
|
||||
|
||||
beforeEach(() => {
|
||||
scheduler = getTestScheduler();
|
||||
|
||||
halService = jasmine.createSpyObj('halService', {
|
||||
getEndpoint: cold('a', { a: endpointURL })
|
||||
});
|
||||
|
||||
responseCacheEntry = new RequestEntry();
|
||||
responseCacheEntry.request = { href: 'https://rest.api/' } as any;
|
||||
responseCacheEntry.response = new RestResponse(true, 200, 'Success');
|
||||
|
||||
requestService = jasmine.createSpyObj('requestService', {
|
||||
generateRequestId: requestUUID,
|
||||
send: true,
|
||||
removeByHrefSubstring: {},
|
||||
getByHref: observableOf(responseCacheEntry),
|
||||
getByUUID: observableOf(responseCacheEntry),
|
||||
setStaleByHrefSubstring: {},
|
||||
});
|
||||
rdbService = jasmine.createSpyObj('rdbService', {
|
||||
buildSingle: hot('a|', {
|
||||
a: supervisionOrderRD
|
||||
}),
|
||||
buildList: hot('a|', {
|
||||
a: paginatedListRD
|
||||
}),
|
||||
buildFromRequestUUID: hot('a|', {
|
||||
a: supervisionOrderRD
|
||||
}),
|
||||
buildFromRequestUUIDAndAwait: hot('a|', {
|
||||
a: supervisionOrderRD
|
||||
})
|
||||
});
|
||||
groupService = jasmine.createSpyObj('groupService', {
|
||||
getBrowseEndpoint: hot('a', {
|
||||
a: groupEndpoint
|
||||
}),
|
||||
getIDHrefObs: cold('a', {
|
||||
a: 'https://rest.api/rest/api/group/groups/' + groupUUID
|
||||
}),
|
||||
});
|
||||
groupService = jasmine.createSpyObj('groupService', {
|
||||
getIDHrefObs: cold('a', {
|
||||
a: 'https://rest.api/rest/api/group/groups/' + groupUUID
|
||||
}),
|
||||
});
|
||||
objectCache = {} as ObjectCacheService;
|
||||
const notificationsService = {} as NotificationsService;
|
||||
const comparator = {} as any;
|
||||
|
||||
service = new SupervisionOrderDataService(
|
||||
requestService,
|
||||
rdbService,
|
||||
objectCache,
|
||||
halService,
|
||||
notificationsService,
|
||||
comparator,
|
||||
groupService,
|
||||
);
|
||||
|
||||
spyOn(service, 'findById').and.callThrough();
|
||||
spyOn(service, 'findByHref').and.callThrough();
|
||||
spyOn(service, 'invalidateByHref').and.returnValue(observableOf(true));
|
||||
spyOn((service as any).createData, 'create').and.callThrough();
|
||||
spyOn((service as any).deleteData, 'delete').and.callThrough();
|
||||
spyOn((service as any).patchData, 'update').and.callThrough();
|
||||
spyOn((service as any).searchData, 'searchBy').and.callThrough();
|
||||
spyOn((service as any).searchData, 'getSearchByHref').and.returnValue(observableOf(requestURL));
|
||||
});
|
||||
|
||||
describe('create', () => {
|
||||
it('should proxy the call to createData.create with group UUID', () => {
|
||||
scheduler.schedule(() => service.create(supervisionOrder, itemUUID, groupUUID, supervisionOrderType));
|
||||
const params = [
|
||||
new RequestParam('uuid', itemUUID),
|
||||
new RequestParam('group', groupUUID),
|
||||
new RequestParam('type', supervisionOrderType),
|
||||
];
|
||||
scheduler.flush();
|
||||
|
||||
expect((service as any).createData.create).toHaveBeenCalledWith(supervisionOrder, ...params);
|
||||
});
|
||||
|
||||
it('should proxy the call to createData.create with group UUID', () => {
|
||||
scheduler.schedule(() => service.create(supervisionOrder, itemUUID, groupUUID, supervisionOrderType));
|
||||
const params = [
|
||||
new RequestParam('uuid', itemUUID),
|
||||
new RequestParam('group', groupUUID),
|
||||
new RequestParam('type', supervisionOrderType),
|
||||
];
|
||||
scheduler.flush();
|
||||
|
||||
expect((service as any).createData.create).toHaveBeenCalledWith(supervisionOrder, ...params);
|
||||
});
|
||||
|
||||
it('should return a RemoteData<supervisionOrder> for the object with the given id', () => {
|
||||
const result = service.create(supervisionOrder, itemUUID, groupUUID, supervisionOrderType);
|
||||
const expected = cold('a|', {
|
||||
a: supervisionOrderRD
|
||||
});
|
||||
expect(result).toBeObservable(expected);
|
||||
});
|
||||
});
|
||||
|
||||
describe('delete', () => {
|
||||
it('should proxy the call to deleteData.delete', () => {
|
||||
scheduler.schedule(() => service.delete(supervisionOrderId));
|
||||
scheduler.flush();
|
||||
|
||||
expect((service as any).deleteData.delete).toHaveBeenCalledWith(supervisionOrderId);
|
||||
});
|
||||
});
|
||||
|
||||
describe('update', () => {
|
||||
it('should proxy the call to updateData.update', () => {
|
||||
scheduler.schedule(() => service.update(supervisionOrder));
|
||||
scheduler.flush();
|
||||
|
||||
expect((service as any).patchData.update).toHaveBeenCalledWith(supervisionOrder);
|
||||
});
|
||||
});
|
||||
|
||||
describe('findById', () => {
|
||||
it('should return a RemoteData<supervisionOrder> for the object with the given id', () => {
|
||||
const result = service.findById(supervisionOrderId);
|
||||
const expected = cold('a|', {
|
||||
a: supervisionOrderRD
|
||||
});
|
||||
expect(result).toBeObservable(expected);
|
||||
});
|
||||
});
|
||||
|
||||
describe('findByHref', () => {
|
||||
it('should return a RemoteData<supervisionOrder> for the object with the given URL', () => {
|
||||
const result = service.findByHref(requestURL);
|
||||
const expected = cold('a|', {
|
||||
a: supervisionOrderRD
|
||||
});
|
||||
expect(result).toBeObservable(expected);
|
||||
});
|
||||
});
|
||||
|
||||
describe('searchByGroup', () => {
|
||||
it('should proxy the call to searchData.searchBy', () => {
|
||||
const options = new FindListOptions();
|
||||
options.searchParams = [new RequestParam('uuid', groupUUID)];
|
||||
scheduler.schedule(() => service.searchByGroup(groupUUID));
|
||||
scheduler.flush();
|
||||
|
||||
expect((service as any).searchData.searchBy).toHaveBeenCalledWith((service as any).searchByGroupMethod, options, true, true);
|
||||
});
|
||||
|
||||
it('should proxy the call to searchData.searchBy with additional search param', () => {
|
||||
const options = new FindListOptions();
|
||||
options.searchParams = [
|
||||
new RequestParam('uuid', groupUUID),
|
||||
new RequestParam('item', itemUUID),
|
||||
];
|
||||
scheduler.schedule(() => service.searchByGroup(groupUUID, itemUUID));
|
||||
scheduler.flush();
|
||||
|
||||
expect((service as any).searchData.searchBy).toHaveBeenCalledWith((service as any).searchByGroupMethod, options, true, true);
|
||||
});
|
||||
|
||||
it('should return a RemoteData<PaginatedList<supervisionOrder>) for the search', () => {
|
||||
const result = service.searchByGroup(groupUUID);
|
||||
const expected = cold('a|', {
|
||||
a: paginatedListRD
|
||||
});
|
||||
expect(result).toBeObservable(expected);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('searchByItem', () => {
|
||||
it('should proxy the call to searchData.searchBy', () => {
|
||||
const options = new FindListOptions();
|
||||
options.searchParams = [new RequestParam('uuid', itemUUID)];
|
||||
scheduler.schedule(() => service.searchByItem(itemUUID));
|
||||
scheduler.flush();
|
||||
|
||||
expect((service as any).searchData.searchBy).toHaveBeenCalledWith((service as any).searchByItemMethod, options, true, true);
|
||||
});
|
||||
|
||||
it('should return a RemoteData<PaginatedList<supervisionOrder>) for the search', () => {
|
||||
const result = service.searchByItem(itemUUID);
|
||||
const expected = cold('a|', {
|
||||
a: paginatedListRD
|
||||
});
|
||||
expect(result).toBeObservable(expected);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
181
src/app/core/supervision-order/supervision-order-data.service.ts
Normal file
181
src/app/core/supervision-order/supervision-order-data.service.ts
Normal file
@@ -0,0 +1,181 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { HttpHeaders } from '@angular/common/http';
|
||||
import { Observable } from 'rxjs';
|
||||
import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model';
|
||||
import { RequestService } from '../data/request.service';
|
||||
import { HALEndpointService } from '../shared/hal-endpoint.service';
|
||||
import { SupervisionOrder } from './models/supervision-order.model';
|
||||
import { RemoteData } from '../data/remote-data';
|
||||
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
|
||||
import { ObjectCacheService } from '../cache/object-cache.service';
|
||||
import { NotificationsService } from '../../shared/notifications/notifications.service';
|
||||
import { SUPERVISION_ORDER } from './models/supervision-order.resource-type';
|
||||
import { DefaultChangeAnalyzer } from '../data/default-change-analyzer.service';
|
||||
import { PaginatedList } from '../data/paginated-list.model';
|
||||
import { RequestParam } from '../cache/models/request-param.model';
|
||||
import { isNotEmpty } from '../../shared/empty.util';
|
||||
import { first, map } from 'rxjs/operators';
|
||||
import { NoContent } from '../shared/NoContent.model';
|
||||
import { getFirstCompletedRemoteData } from '../shared/operators';
|
||||
import { FindListOptions } from '../data/find-list-options.model';
|
||||
import { HttpOptions } from '../dspace-rest/dspace-rest.service';
|
||||
import { PutRequest } from '../data/request.models';
|
||||
import { GenericConstructor } from '../shared/generic-constructor';
|
||||
import { ResponseParsingService } from '../data/parsing.service';
|
||||
import { StatusCodeOnlyResponseParsingService } from '../data/status-code-only-response-parsing.service';
|
||||
import { GroupDataService } from '../eperson/group-data.service';
|
||||
import { IdentifiableDataService } from '../data/base/identifiable-data.service';
|
||||
import { CreateDataImpl } from '../data/base/create-data';
|
||||
import { SearchDataImpl } from '../data/base/search-data';
|
||||
import { PatchDataImpl } from '../data/base/patch-data';
|
||||
import { DeleteDataImpl } from '../data/base/delete-data';
|
||||
import { dataService } from '../data/base/data-service.decorator';
|
||||
|
||||
/**
|
||||
* A service responsible for fetching/sending data from/to the REST API on the supervisionorders endpoint
|
||||
*/
|
||||
@Injectable()
|
||||
@dataService(SUPERVISION_ORDER)
|
||||
export class SupervisionOrderDataService extends IdentifiableDataService<SupervisionOrder> {
|
||||
protected searchByGroupMethod = 'group';
|
||||
protected searchByItemMethod = 'byItem';
|
||||
|
||||
private createData: CreateDataImpl<SupervisionOrder>;
|
||||
private searchData: SearchDataImpl<SupervisionOrder>;
|
||||
private patchData: PatchDataImpl<SupervisionOrder>;
|
||||
private deleteData: DeleteDataImpl<SupervisionOrder>;
|
||||
|
||||
constructor(
|
||||
protected requestService: RequestService,
|
||||
protected rdbService: RemoteDataBuildService,
|
||||
protected objectCache: ObjectCacheService,
|
||||
protected halService: HALEndpointService,
|
||||
protected notificationsService: NotificationsService,
|
||||
protected comparator: DefaultChangeAnalyzer<SupervisionOrder>,
|
||||
protected groupService: GroupDataService,
|
||||
) {
|
||||
super('supervisionorders', requestService, rdbService, objectCache, halService);
|
||||
|
||||
this.createData = new CreateDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, notificationsService, this.responseMsToLive);
|
||||
this.searchData = new SearchDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, this.responseMsToLive);
|
||||
this.patchData = new PatchDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, comparator, this.responseMsToLive, this.constructIdEndpoint);
|
||||
this.deleteData = new DeleteDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, notificationsService, this.responseMsToLive, this.constructIdEndpoint);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new SupervisionOrder on the server, and store the response
|
||||
* in the object cache
|
||||
*
|
||||
* @param {SupervisionOrder} supervisionOrder
|
||||
* The supervision order to create
|
||||
* @param {string} itemUUID
|
||||
* The uuid of the item that will be grant of the permission.
|
||||
* @param {string} groupUUID
|
||||
* The uuid of the group that will be grant of the permission.
|
||||
* @param {string} type
|
||||
* The type of the supervision order that will be grant of the permission.
|
||||
*/
|
||||
create(supervisionOrder: SupervisionOrder, itemUUID: string, groupUUID: string, type: string): Observable<RemoteData<SupervisionOrder>> {
|
||||
const params = [];
|
||||
params.push(new RequestParam('uuid', itemUUID));
|
||||
params.push(new RequestParam('group', groupUUID));
|
||||
params.push(new RequestParam('type', type));
|
||||
return this.createData.create(supervisionOrder, ...params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete an existing SupervisionOrder on the server
|
||||
*
|
||||
* @param supervisionOrderID The supervision order's id to be removed
|
||||
* @return an observable that emits true when the deletion was successful, false when it failed
|
||||
*/
|
||||
delete(supervisionOrderID: string): Observable<boolean> {
|
||||
return this.deleteData.delete(supervisionOrderID).pipe(
|
||||
getFirstCompletedRemoteData(),
|
||||
map((response: RemoteData<NoContent>) => response.hasSucceeded),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new patch to the object cache
|
||||
* The patch is derived from the differences between the given object and its version in the object cache
|
||||
* @param {SupervisionOrder} object The given object
|
||||
*/
|
||||
update(object: SupervisionOrder): Observable<RemoteData<SupervisionOrder>> {
|
||||
return this.patchData.update(object);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the {@link SupervisionOrder} list for a {@link Group}
|
||||
*
|
||||
* @param UUID UUID of a given {@link Group}
|
||||
* @param itemUUID Limit the returned policies to the specified DSO
|
||||
* @param useCachedVersionIfAvailable If this is true, the request will only be sent if there's
|
||||
* no valid cached version. Defaults to true
|
||||
* @param reRequestOnStale Whether or not the request should automatically be re-
|
||||
* requested after the response becomes stale
|
||||
* @param linksToFollow List of {@link FollowLinkConfig} that indicate which
|
||||
* {@link HALLink}s should be automatically resolved
|
||||
*/
|
||||
searchByGroup(UUID: string, itemUUID?: string, useCachedVersionIfAvailable = true, reRequestOnStale = true, ...linksToFollow: FollowLinkConfig<SupervisionOrder>[]): Observable<RemoteData<PaginatedList<SupervisionOrder>>> {
|
||||
const options = new FindListOptions();
|
||||
options.searchParams = [new RequestParam('uuid', UUID)];
|
||||
if (isNotEmpty(itemUUID)) {
|
||||
options.searchParams.push(new RequestParam('item', itemUUID));
|
||||
}
|
||||
return this.searchData.searchBy(this.searchByGroupMethod, options, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the {@link SupervisionOrder} list for a given DSO
|
||||
*
|
||||
* @param UUID UUID of a given DSO
|
||||
* @param action Limit the returned policies to the specified {@link ActionType}
|
||||
* @param useCachedVersionIfAvailable If this is true, the request will only be sent if there's
|
||||
* no valid cached version. Defaults to true
|
||||
* @param reRequestOnStale Whether or not the request should automatically be re-
|
||||
* requested after the response becomes stale
|
||||
* @param linksToFollow List of {@link FollowLinkConfig} that indicate which
|
||||
* {@link HALLink}s should be automatically resolved
|
||||
*/
|
||||
searchByItem(UUID: string, useCachedVersionIfAvailable = true, reRequestOnStale = true, ...linksToFollow: FollowLinkConfig<SupervisionOrder>[]): Observable<RemoteData<PaginatedList<SupervisionOrder>>> {
|
||||
const options = new FindListOptions();
|
||||
options.searchParams = [new RequestParam('uuid', UUID)];
|
||||
return this.searchData.searchBy(this.searchByItemMethod, options, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the target of the supervision order
|
||||
* @param supervisionOrderId the ID of the supervision order
|
||||
* @param supervisionOrderHref the link to the supervision order
|
||||
* @param targetUUID the UUID of the target to which the permission is being granted
|
||||
* @param targetType the type of the target (eperson or group) to which the permission is being granted
|
||||
*/
|
||||
updateTarget(supervisionOrderId: string, supervisionOrderHref: string, targetUUID: string, targetType: string): Observable<RemoteData<any>> {
|
||||
const targetService = this.groupService;
|
||||
const targetEndpoint$ = targetService.getIDHrefObs(targetUUID);
|
||||
|
||||
const options: HttpOptions = Object.create({});
|
||||
let headers = new HttpHeaders();
|
||||
headers = headers.append('Content-Type', 'text/uri-list');
|
||||
options.headers = headers;
|
||||
|
||||
const requestId = this.requestService.generateRequestId();
|
||||
|
||||
targetEndpoint$.pipe(
|
||||
first(),
|
||||
).subscribe((targetEndpoint) => {
|
||||
const resourceEndpoint = supervisionOrderHref + '/' + targetType;
|
||||
const request = new PutRequest(requestId, resourceEndpoint, targetEndpoint, options);
|
||||
Object.assign(request, {
|
||||
getResponseParser(): GenericConstructor<ResponseParsingService> {
|
||||
return StatusCodeOnlyResponseParsingService;
|
||||
}
|
||||
});
|
||||
this.requestService.send(request);
|
||||
});
|
||||
|
||||
return this.rdbService.buildFromRequestUUIDAndAwait(requestId, () => this.invalidateByHref(supervisionOrderHref));
|
||||
}
|
||||
|
||||
}
|
@@ -1,4 +1,5 @@
|
||||
export enum MyDSpaceConfigurationValueType {
|
||||
Workspace = 'workspace',
|
||||
SupervisedItems = 'supervisedWorkspace',
|
||||
Workflow = 'workflow'
|
||||
}
|
||||
|
@@ -198,6 +198,7 @@ describe('MyDSpaceConfigurationService', () => {
|
||||
|
||||
expect(list$).toBeObservable(cold('(b|)', {
|
||||
b: [
|
||||
MyDSpaceConfigurationValueType.SupervisedItems,
|
||||
MyDSpaceConfigurationValueType.Workflow
|
||||
]
|
||||
}));
|
||||
@@ -212,6 +213,7 @@ describe('MyDSpaceConfigurationService', () => {
|
||||
|
||||
expect(list$).toBeObservable(cold('(b|)', {
|
||||
b: [
|
||||
MyDSpaceConfigurationValueType.SupervisedItems,
|
||||
MyDSpaceConfigurationValueType.Workflow
|
||||
]
|
||||
}));
|
||||
@@ -227,6 +229,7 @@ describe('MyDSpaceConfigurationService', () => {
|
||||
expect(list$).toBeObservable(cold('(b|)', {
|
||||
b: [
|
||||
MyDSpaceConfigurationValueType.Workspace,
|
||||
MyDSpaceConfigurationValueType.SupervisedItems,
|
||||
MyDSpaceConfigurationValueType.Workflow
|
||||
]
|
||||
}));
|
||||
|
@@ -20,6 +20,7 @@ import { Context } from '../core/shared/context.model';
|
||||
|
||||
export const MyDSpaceConfigurationToContextMap = new Map([
|
||||
[MyDSpaceConfigurationValueType.Workspace, Context.Workspace],
|
||||
[MyDSpaceConfigurationValueType.SupervisedItems, Context.SupervisedItems],
|
||||
[MyDSpaceConfigurationValueType.Workflow, Context.Workflow]
|
||||
]);
|
||||
|
||||
@@ -109,6 +110,7 @@ export class MyDSpaceConfigurationService extends SearchConfigurationService {
|
||||
availableConf.push(MyDSpaceConfigurationValueType.Workspace);
|
||||
}
|
||||
if (isController || isAdmin) {
|
||||
availableConf.push(MyDSpaceConfigurationValueType.SupervisedItems);
|
||||
availableConf.push(MyDSpaceConfigurationValueType.Workflow);
|
||||
}
|
||||
return availableConf;
|
||||
|
@@ -128,7 +128,6 @@ export class ProcessDetailComponent implements OnInit {
|
||||
* Sets the outputLogs when retrieved and sets the showOutputLogs boolean to show them and hide the button.
|
||||
*/
|
||||
showProcessOutputLogs() {
|
||||
console.log('showProcessOutputLogs');
|
||||
this.retrievingOutputLogs$.next(true);
|
||||
this.zone.runOutsideAngular(() => {
|
||||
const processOutputRD$: Observable<RemoteData<Bitstream>> = this.processRD$.pipe(
|
||||
|
@@ -6,21 +6,21 @@ import { TranslateModule } from '@ngx-translate/core';
|
||||
import { cold } from 'jasmine-marbles';
|
||||
import uniqueId from 'lodash/uniqueId';
|
||||
|
||||
import { createSuccessfulRemoteDataObject } from '../../../remote-data.utils';
|
||||
import { createTestComponent } from '../../../testing/utils.test';
|
||||
import { EPersonDataService } from '../../../../core/eperson/eperson-data.service';
|
||||
import { GroupDataService } from '../../../../core/eperson/group-data.service';
|
||||
import { RequestService } from '../../../../core/data/request.service';
|
||||
import { getMockRequestService } from '../../../mocks/request.service.mock';
|
||||
import { createSuccessfulRemoteDataObject } from '../remote-data.utils';
|
||||
import { createTestComponent } from '../testing/utils.test';
|
||||
import { EPersonDataService } from '../../core/eperson/eperson-data.service';
|
||||
import { GroupDataService } from '../../core/eperson/group-data.service';
|
||||
import { RequestService } from '../../core/data/request.service';
|
||||
import { getMockRequestService } from '../mocks/request.service.mock';
|
||||
import { EpersonGroupListComponent, SearchEvent } from './eperson-group-list.component';
|
||||
import { EPersonMock } from '../../../testing/eperson.mock';
|
||||
import { GroupMock } from '../../../testing/group-mock';
|
||||
import { PaginationComponentOptions } from '../../../pagination/pagination-component-options.model';
|
||||
import { buildPaginatedList } from '../../../../core/data/paginated-list.model';
|
||||
import { PageInfo } from '../../../../core/shared/page-info.model';
|
||||
import { EPersonMock } from '../testing/eperson.mock';
|
||||
import { GroupMock } from '../testing/group-mock';
|
||||
import { PaginationComponentOptions } from '../pagination/pagination-component-options.model';
|
||||
import { buildPaginatedList } from '../../core/data/paginated-list.model';
|
||||
import { PageInfo } from '../../core/shared/page-info.model';
|
||||
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
|
||||
import { PaginationService } from '../../../../core/pagination/pagination.service';
|
||||
import { PaginationServiceStub } from '../../../testing/pagination-service.stub';
|
||||
import { PaginationService } from '../../core/pagination/pagination.service';
|
||||
import { PaginationServiceStub } from '../testing/pagination-service.stub';
|
||||
|
||||
describe('EpersonGroupListComponent test suite', () => {
|
||||
let comp: EpersonGroupListComponent;
|
@@ -4,22 +4,22 @@ import { BehaviorSubject, Observable, Subscription } from 'rxjs';
|
||||
import { map } from 'rxjs/operators';
|
||||
import uniqueId from 'lodash/uniqueId';
|
||||
|
||||
import { RemoteData } from '../../../../core/data/remote-data';
|
||||
import { PaginatedList } from '../../../../core/data/paginated-list.model';
|
||||
import { DSpaceObject } from '../../../../core/shared/dspace-object.model';
|
||||
import { PaginationComponentOptions } from '../../../pagination/pagination-component-options.model';
|
||||
import { hasValue, isNotEmpty } from '../../../empty.util';
|
||||
import { DSONameService } from '../../../../core/breadcrumbs/dso-name.service';
|
||||
import { EPERSON } from '../../../../core/eperson/models/eperson.resource-type';
|
||||
import { GROUP } from '../../../../core/eperson/models/group.resource-type';
|
||||
import { ResourceType } from '../../../../core/shared/resource-type';
|
||||
import { EPersonDataService } from '../../../../core/eperson/eperson-data.service';
|
||||
import { GroupDataService } from '../../../../core/eperson/group-data.service';
|
||||
import { fadeInOut } from '../../../animations/fade';
|
||||
import { getFirstCompletedRemoteData } from '../../../../core/shared/operators';
|
||||
import { PaginationService } from '../../../../core/pagination/pagination.service';
|
||||
import { FindListOptions } from '../../../../core/data/find-list-options.model';
|
||||
import { getDataServiceFor } from '../../../../core/data/base/data-service.decorator';
|
||||
import { RemoteData } from '../../core/data/remote-data';
|
||||
import { PaginatedList } from '../../core/data/paginated-list.model';
|
||||
import { DSpaceObject } from '../../core/shared/dspace-object.model';
|
||||
import { PaginationComponentOptions } from '../pagination/pagination-component-options.model';
|
||||
import { hasValue, isNotEmpty } from '../empty.util';
|
||||
import { DSONameService } from '../../core/breadcrumbs/dso-name.service';
|
||||
import { EPERSON } from '../../core/eperson/models/eperson.resource-type';
|
||||
import { GROUP } from '../../core/eperson/models/group.resource-type';
|
||||
import { ResourceType } from '../../core/shared/resource-type';
|
||||
import { EPersonDataService } from '../../core/eperson/eperson-data.service';
|
||||
import { GroupDataService } from '../../core/eperson/group-data.service';
|
||||
import { fadeInOut } from '../animations/fade';
|
||||
import { getFirstCompletedRemoteData } from '../../core/shared/operators';
|
||||
import { PaginationService } from '../../core/pagination/pagination.service';
|
||||
import { FindListOptions } from '../../core/data/find-list-options.model';
|
||||
import { getDataServiceFor } from '../../core/data/base/data-service.decorator';
|
||||
|
||||
export interface SearchEvent {
|
||||
scope: string;
|
@@ -4,7 +4,7 @@ import { FormBuilder, FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
|
||||
import { createTestComponent } from '../../../../testing/utils.test';
|
||||
import { createTestComponent } from '../../testing/utils.test';
|
||||
import { EpersonSearchBoxComponent } from './eperson-search-box.component';
|
||||
import { SearchEvent } from '../eperson-group-list.component';
|
||||
|
@@ -4,7 +4,7 @@ import { FormBuilder } from '@angular/forms';
|
||||
import { Subscription } from 'rxjs';
|
||||
|
||||
import { SearchEvent } from '../eperson-group-list.component';
|
||||
import { isNotNull } from '../../../../empty.util';
|
||||
import { isNotNull } from '../../empty.util';
|
||||
|
||||
/**
|
||||
* A component used to show a search box for epersons.
|
@@ -4,7 +4,7 @@ import { FormBuilder, FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
|
||||
import { createTestComponent } from '../../../../testing/utils.test';
|
||||
import { createTestComponent } from '../../testing/utils.test';
|
||||
import { GroupSearchBoxComponent } from './group-search-box.component';
|
||||
import { SearchEvent } from '../eperson-group-list.component';
|
||||
|
@@ -4,7 +4,7 @@ import { FormBuilder } from '@angular/forms';
|
||||
import { Subscription } from 'rxjs';
|
||||
|
||||
import { SearchEvent } from '../eperson-group-list.component';
|
||||
import { isNotNull } from '../../../../empty.util';
|
||||
import { isNotNull } from '../../empty.util';
|
||||
|
||||
/**
|
||||
* A component used to show a search box for groups.
|
@@ -25,7 +25,11 @@
|
||||
<ds-importable-list-item-control *ngIf="importable" [object]="object"
|
||||
[importConfig]="importConfig"
|
||||
(importObject)="importObject.emit($event)"></ds-importable-list-item-control>
|
||||
<ds-listable-object-component-loader [object]="object" [viewMode]="viewMode" [index]="i" [context]="context" [linkType]="linkType"
|
||||
<ds-listable-object-component-loader [object]="object"
|
||||
[viewMode]="viewMode"
|
||||
[index]="i"
|
||||
[context]="context"
|
||||
[linkType]="linkType"
|
||||
[listID]="selectionConfig?.listId"
|
||||
(contentChange)="contentChange.emit($event)"></ds-listable-object-component-loader>
|
||||
</li>
|
||||
|
@@ -23,19 +23,19 @@
|
||||
<span *ngIf="linkType == linkTypes.None" class="lead item-list-title dont-break-out"
|
||||
[innerHTML]="dsoTitle"></span>
|
||||
<span class="text-muted">
|
||||
<ds-truncatable-part [id]="dso.id" [minLines]="1">
|
||||
<ng-container *ngIf="dso.firstMetadataValue('dc.publisher') || dso.firstMetadataValue('dc.date.issued')">
|
||||
(<span *ngIf="dso.firstMetadataValue('dc.publisher')" class="item-list-publisher" [innerHTML]="firstMetadataValue('dc.publisher') + ', '"></span>
|
||||
<span *ngIf="dso.firstMetadataValue('dc.date.issued')" class="item-list-date" [innerHTML]="firstMetadataValue('dc.date.issued')"></span>)
|
||||
</ng-container>
|
||||
<span *ngIf="dso.allMetadata(['dc.contributor.author', 'dc.creator', 'dc.contributor.*']).length > 0" class="item-list-authors">
|
||||
<span *ngFor="let author of allMetadataValues(['dc.contributor.author', 'dc.creator', 'dc.contributor.*']); let last=last;">
|
||||
<span [innerHTML]="author"><span [innerHTML]="author"></span></span>
|
||||
<span *ngIf="!last">; </span>
|
||||
<ds-truncatable-part [id]="dso.id" [minLines]="1">
|
||||
<ng-container *ngIf="dso.firstMetadataValue('dc.publisher') || dso.firstMetadataValue('dc.date.issued')">
|
||||
(<span *ngIf="dso.firstMetadataValue('dc.publisher')" class="item-list-publisher" [innerHTML]="firstMetadataValue('dc.publisher') + ', '"></span>
|
||||
<span *ngIf="dso.firstMetadataValue('dc.date.issued')" class="item-list-date" [innerHTML]="firstMetadataValue('dc.date.issued')"></span>)
|
||||
</ng-container>
|
||||
<span *ngIf="dso.allMetadata(['dc.contributor.author', 'dc.creator', 'dc.contributor.*']).length > 0" class="item-list-authors">
|
||||
<span *ngFor="let author of allMetadataValues(['dc.contributor.author', 'dc.creator', 'dc.contributor.*']); let last=last;">
|
||||
<span [innerHTML]="author"><span [innerHTML]="author"></span></span>
|
||||
<span *ngIf="!last">; </span>
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</ds-truncatable-part>
|
||||
</span>
|
||||
</ds-truncatable-part>
|
||||
</span>
|
||||
<div *ngIf="dso.firstMetadataValue('dc.description.abstract')" class="item-list-abstract">
|
||||
<ds-truncatable-part [id]="dso.id" [minLines]="3"><span
|
||||
[innerHTML]="firstMetadataValue('dc.description.abstract')"></span>
|
||||
|
@@ -1 +1,7 @@
|
||||
@import '../../../../../../../styles/variables';
|
||||
|
||||
.item-list-supervision {
|
||||
a {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
@@ -24,9 +24,9 @@ import { ResourcePolicyEvent, ResourcePolicyFormComponent } from './resource-pol
|
||||
import { FormService } from '../../form/form.service';
|
||||
import { getMockFormService } from '../../mocks/form-service.mock';
|
||||
import { FormBuilderService } from '../../form/builder/form-builder.service';
|
||||
import { EpersonGroupListComponent } from './eperson-group-list/eperson-group-list.component';
|
||||
import { EpersonGroupListComponent } from '../../eperson-group-list/eperson-group-list.component';
|
||||
import { FormComponent } from '../../form/form.component';
|
||||
import { stringToNgbDateStruct, dateToISOFormat } from '../../date.util';
|
||||
import { dateToISOFormat, stringToNgbDateStruct } from '../../date.util';
|
||||
import { ResourcePolicy } from '../../../core/resource-policy/models/resource-policy.model';
|
||||
import { RESOURCE_POLICY } from '../../../core/resource-policy/models/resource-policy.resource-type';
|
||||
import { EPersonMock } from '../../testing/eperson.mock';
|
||||
|
@@ -10,9 +10,6 @@ import { ResourcePolicyCreateComponent } from './create/resource-policy-create.c
|
||||
import { FormModule } from '../form/form.module';
|
||||
import { ResourcePolicyResolver } from './resolvers/resource-policy.resolver';
|
||||
import { ResourcePolicyTargetResolver } from './resolvers/resource-policy-target.resolver';
|
||||
import { EpersonGroupListComponent } from './form/eperson-group-list/eperson-group-list.component';
|
||||
import { GroupSearchBoxComponent } from './form/eperson-group-list/group-search-box/group-search-box.component';
|
||||
import { EpersonSearchBoxComponent } from './form/eperson-group-list/eperson-search-box/eperson-search-box.component';
|
||||
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
|
||||
import { SharedModule } from '../shared.module';
|
||||
import { ResourcePolicyEntryComponent } from './entry/resource-policy-entry.component';
|
||||
@@ -23,9 +20,6 @@ const COMPONENTS = [
|
||||
ResourcePolicyFormComponent,
|
||||
ResourcePolicyEditComponent,
|
||||
ResourcePolicyCreateComponent,
|
||||
EpersonGroupListComponent,
|
||||
EpersonSearchBoxComponent,
|
||||
GroupSearchBoxComponent
|
||||
];
|
||||
|
||||
const PROVIDERS = [
|
||||
|
@@ -34,6 +34,7 @@ import { CollectionElementLinkType } from '../object-collection/collection-eleme
|
||||
import { environment } from 'src/environments/environment';
|
||||
import { SubmissionObject } from '../../core/submission/models/submission-object.model';
|
||||
import { SearchFilterConfig } from './models/search-filter-config.model';
|
||||
import { WorkspaceItem } from '../..//core/submission/models/workspaceitem.model';
|
||||
|
||||
@Component({
|
||||
selector: 'ds-search',
|
||||
@@ -398,15 +399,21 @@ export class SearchComponent implements OnInit {
|
||||
private retrieveSearchResults(searchOptions: PaginatedSearchOptions) {
|
||||
this.resultsRD$.next(null);
|
||||
this.lastSearchOptions = searchOptions;
|
||||
let followLinks = [
|
||||
followLink<Item>('thumbnail', { isOptional: true }),
|
||||
followLink<SubmissionObject>('item', { isOptional: true }, followLink<Item>('thumbnail', { isOptional: true })) as any,
|
||||
followLink<Item>('accessStatus', { isOptional: true, shouldEmbed: environment.item.showAccessStatuses }),
|
||||
];
|
||||
if (this.configuration === 'supervision') {
|
||||
followLinks.push(followLink<WorkspaceItem>('supervisionOrders', { isOptional: true }) as any);
|
||||
}
|
||||
this.service.search(
|
||||
searchOptions,
|
||||
undefined,
|
||||
this.useCachedVersionIfAvailable,
|
||||
true,
|
||||
followLink<Item>('thumbnail', { isOptional: true }),
|
||||
followLink<SubmissionObject>('item', { isOptional: true }, followLink<Item>('thumbnail', { isOptional: true })) as any,
|
||||
followLink<Item>('accessStatus', { isOptional: true, shouldEmbed: environment.item.showAccessStatuses })
|
||||
).pipe(getFirstCompletedRemoteData())
|
||||
...followLinks
|
||||
).pipe(getFirstCompletedRemoteData())
|
||||
.subscribe((results: RemoteData<SearchObjects<DSpaceObject>>) => {
|
||||
if (results.hasSucceeded) {
|
||||
if (this.trackStatistics) {
|
||||
|
@@ -84,8 +84,9 @@ import { LangSwitchComponent } from './lang-switch/lang-switch.component';
|
||||
import {
|
||||
PlainTextMetadataListElementComponent
|
||||
} from './object-list/metadata-representation-list-element/plain-text/plain-text-metadata-list-element.component';
|
||||
import { BrowseLinkMetadataListElementComponent }
|
||||
from './object-list/metadata-representation-list-element/browse-link/browse-link-metadata-list-element.component';
|
||||
import {
|
||||
BrowseLinkMetadataListElementComponent
|
||||
} from './object-list/metadata-representation-list-element/browse-link/browse-link-metadata-list-element.component';
|
||||
import {
|
||||
ItemMetadataListElementComponent
|
||||
} from './object-list/metadata-representation-list-element/item/item-metadata-list-element.component';
|
||||
@@ -246,8 +247,12 @@ import {
|
||||
import { ThemedCollectionDropdownComponent } from './collection-dropdown/themed-collection-dropdown.component';
|
||||
import { MetadataFieldWrapperComponent } from './metadata-field-wrapper/metadata-field-wrapper.component';
|
||||
import { ShortNumberPipe } from './utils/short-number.pipe';
|
||||
import { LogInExternalProviderComponent } from './log-in/methods/log-in-external-provider/log-in-external-provider.component';
|
||||
import { AdvancedClaimedTaskActionSelectReviewerComponent } from './mydspace-actions/claimed-task/select-reviewer/advanced-claimed-task-action-select-reviewer.component';
|
||||
import {
|
||||
LogInExternalProviderComponent
|
||||
} from './log-in/methods/log-in-external-provider/log-in-external-provider.component';
|
||||
import {
|
||||
AdvancedClaimedTaskActionSelectReviewerComponent
|
||||
} from './mydspace-actions/claimed-task/select-reviewer/advanced-claimed-task-action-select-reviewer.component';
|
||||
import {
|
||||
AdvancedClaimedTaskActionRatingComponent
|
||||
} from './mydspace-actions/claimed-task/rating/advanced-claimed-task-action-rating.component';
|
||||
@@ -255,7 +260,9 @@ import { ClaimedTaskActionsDeclineTaskComponent } from './mydspace-actions/claim
|
||||
import {
|
||||
DsoPageSubscriptionButtonComponent
|
||||
} from './dso-page/dso-page-subscription-button/dso-page-subscription-button.component';
|
||||
|
||||
import { EpersonGroupListComponent } from './eperson-group-list/eperson-group-list.component';
|
||||
import { EpersonSearchBoxComponent } from './eperson-group-list/eperson-search-box/eperson-search-box.component';
|
||||
import { GroupSearchBoxComponent } from './eperson-group-list/group-search-box/group-search-box.component';
|
||||
|
||||
const MODULES = [
|
||||
CommonModule,
|
||||
@@ -358,6 +365,9 @@ const COMPONENTS = [
|
||||
DsoPageSubscriptionButtonComponent,
|
||||
MetadataFieldWrapperComponent,
|
||||
ContextHelpWrapperComponent,
|
||||
EpersonGroupListComponent,
|
||||
EpersonSearchBoxComponent,
|
||||
GroupSearchBoxComponent
|
||||
];
|
||||
|
||||
const ENTRY_COMPONENTS = [
|
||||
@@ -416,6 +426,9 @@ const ENTRY_COMPONENTS = [
|
||||
ListableNotificationObjectComponent,
|
||||
AdvancedClaimedTaskActionSelectReviewerComponent,
|
||||
AdvancedClaimedTaskActionRatingComponent,
|
||||
EpersonGroupListComponent,
|
||||
EpersonSearchBoxComponent,
|
||||
GroupSearchBoxComponent
|
||||
];
|
||||
|
||||
const PROVIDERS = [
|
||||
|
95
src/app/shared/testing/supervision-order.mock.ts
Normal file
95
src/app/shared/testing/supervision-order.mock.ts
Normal file
@@ -0,0 +1,95 @@
|
||||
import { Item } from '../../core/shared/item.model';
|
||||
import { SupervisionOrder } from '../../core/supervision-order/models/supervision-order.model';
|
||||
import { createSuccessfulRemoteDataObject, createSuccessfulRemoteDataObject$ } from '../remote-data.utils';
|
||||
import { GroupMock, GroupMock2 } from './group-mock';
|
||||
import { buildPaginatedList } from '../../core/data/paginated-list.model';
|
||||
import { PageInfo } from '../../core/shared/page-info.model';
|
||||
|
||||
const itemMock = Object.assign(new Item(), {
|
||||
metadata: {
|
||||
'dc.title': [
|
||||
{
|
||||
value: 'Item one'
|
||||
}
|
||||
],
|
||||
'dc.contributor.author': [
|
||||
{
|
||||
value: 'Smith, Donald'
|
||||
}
|
||||
],
|
||||
'dc.publisher': [
|
||||
{
|
||||
value: 'a publisher'
|
||||
}
|
||||
],
|
||||
'dc.date.issued': [
|
||||
{
|
||||
value: '2015-06-26'
|
||||
}
|
||||
],
|
||||
'dc.description.abstract': [
|
||||
{
|
||||
value: 'This is the abstract'
|
||||
}
|
||||
]
|
||||
}
|
||||
});
|
||||
|
||||
const anotherItemMock = Object.assign(new Item(), {
|
||||
metadata: {
|
||||
'dc.title': [
|
||||
{
|
||||
value: 'Item two'
|
||||
}
|
||||
],
|
||||
'dc.contributor.author': [
|
||||
{
|
||||
value: 'Smith, Donald'
|
||||
}
|
||||
],
|
||||
'dc.publisher': [
|
||||
{
|
||||
value: 'a publisher'
|
||||
}
|
||||
],
|
||||
'dc.date.issued': [
|
||||
{
|
||||
value: '2015-06-26'
|
||||
}
|
||||
],
|
||||
'dc.description.abstract': [
|
||||
{
|
||||
value: 'This is the abstract'
|
||||
}
|
||||
]
|
||||
}
|
||||
});
|
||||
|
||||
export const supervisionOrderMock: any = Object.assign(new SupervisionOrder(),{
|
||||
id: '1',
|
||||
item: createSuccessfulRemoteDataObject$(itemMock),
|
||||
group: createSuccessfulRemoteDataObject$(GroupMock)
|
||||
});
|
||||
|
||||
export const anotherSupervisionOrderMock: any = {
|
||||
id: '2',
|
||||
item: createSuccessfulRemoteDataObject$(anotherItemMock),
|
||||
group: createSuccessfulRemoteDataObject$(GroupMock2)
|
||||
};
|
||||
|
||||
export const supervisionOrderListMock = [supervisionOrderMock, anotherSupervisionOrderMock];
|
||||
export const supervisionOrderEntryMock = {
|
||||
supervisionOrder: supervisionOrderMock,
|
||||
group: GroupMock
|
||||
};
|
||||
|
||||
const pageInfo = new PageInfo({
|
||||
elementsPerPage: 10,
|
||||
totalElements: 2,
|
||||
totalPages: 1,
|
||||
currentPage: 1
|
||||
});
|
||||
const array = [supervisionOrderMock, anotherSupervisionOrderMock];
|
||||
const paginatedList = buildPaginatedList(pageInfo, array);
|
||||
export const supervisionOrderPaginatedListRD = createSuccessfulRemoteDataObject(paginatedList);
|
||||
export const supervisionOrderPaginatedListRD$ = createSuccessfulRemoteDataObject$(paginatedList);
|
@@ -540,10 +540,16 @@
|
||||
|
||||
"admin.workflow.item.workflow": "Workflow",
|
||||
|
||||
"admin.workflow.item.workspace": "Workspace",
|
||||
|
||||
"admin.workflow.item.delete": "Delete",
|
||||
|
||||
"admin.workflow.item.send-back": "Send back",
|
||||
|
||||
"admin.workflow.item.policies": "Policies",
|
||||
|
||||
"admin.workflow.item.supervision": "Supervision",
|
||||
|
||||
|
||||
|
||||
"admin.metadata-import.breadcrumbs": "Import Metadata",
|
||||
@@ -1500,6 +1506,32 @@
|
||||
|
||||
"dso-selector.results-could-not-be-retrieved": "Something went wrong, please refresh again ↻",
|
||||
|
||||
"supervision-group-selector.header": "Supervision Group Selector",
|
||||
|
||||
"supervision-group-selector.select.type-of-order.label": "Select a type of Order",
|
||||
|
||||
"supervision-group-selector.select.type-of-order.option.none": "NONE",
|
||||
|
||||
"supervision-group-selector.select.type-of-order.option.editor": "EDITOR",
|
||||
|
||||
"supervision-group-selector.select.type-of-order.option.observer": "OBSERVER",
|
||||
|
||||
"supervision-group-selector.select.group.label": "Select a Group",
|
||||
|
||||
"supervision-group-selector.button.cancel": "Cancel",
|
||||
|
||||
"supervision-group-selector.button.save": "Save",
|
||||
|
||||
"supervision-group-selector.select.type-of-order.error": "Please select a type of order",
|
||||
|
||||
"supervision-group-selector.select.group.error": "Please select a group",
|
||||
|
||||
"supervision-group-selector.notification.create.success.title": "Successfully created supervision order for group {{ name }}",
|
||||
|
||||
"supervision-group-selector.notification.create.failure.title": "Error",
|
||||
|
||||
"supervision-group-selector.notification.create.already-existing" : "A supervision order already exists on this item for selected group",
|
||||
|
||||
"confirmation-modal.export-metadata.header": "Export metadata for {{ dsoName }}",
|
||||
|
||||
"confirmation-modal.export-metadata.info": "Are you sure you want to export metadata for {{ dsoName }}",
|
||||
@@ -2363,6 +2395,22 @@
|
||||
|
||||
"item.truncatable-part.show-less": "Collapse",
|
||||
|
||||
"workflow-item.search.result.delete-supervision.modal.header": "Delete Supervision Order",
|
||||
|
||||
"workflow-item.search.result.delete-supervision.modal.info": "Are you sure you want to delete Supervision Order",
|
||||
|
||||
"workflow-item.search.result.delete-supervision.modal.cancel": "Cancel",
|
||||
|
||||
"workflow-item.search.result.delete-supervision.modal.confirm": "Delete",
|
||||
|
||||
"workflow-item.search.result.notification.deleted.success": "Successfully deleted supervision order \"{{name}}\"",
|
||||
|
||||
"workflow-item.search.result.notification.deleted.failure": "Failed to delete supervision order \"{{name}}\"",
|
||||
|
||||
"workflow-item.search.result.list.element.supervised-by": "Supervised by:",
|
||||
|
||||
"workflow-item.search.result.list.element.supervised.remove-tooltip": "Remove supervision group",
|
||||
|
||||
|
||||
|
||||
"item.page.abstract": "Abstract",
|
||||
@@ -3048,6 +3096,8 @@
|
||||
|
||||
"mydspace.show.workspace": "Your Submissions",
|
||||
|
||||
"mydspace.show.supervisedWorkspace": "Supervised items",
|
||||
|
||||
"mydspace.status.archived": "Archived",
|
||||
|
||||
"mydspace.status.validation": "Validation",
|
||||
@@ -3704,6 +3754,8 @@
|
||||
|
||||
"search.filters.applied.f.birthDate.min": "Start birth date",
|
||||
|
||||
"search.filters.applied.f.supervisedBy": "Supervised by",
|
||||
|
||||
"search.filters.applied.f.withdrawn": "Withdrawn",
|
||||
|
||||
|
||||
@@ -3848,6 +3900,13 @@
|
||||
|
||||
"search.filters.filter.show-tree": "Browse {{ name }} tree",
|
||||
|
||||
"search.filters.filter.supervisedBy.head": "Supervised By",
|
||||
|
||||
"search.filters.filter.supervisedBy.placeholder": "Supervised By",
|
||||
|
||||
"search.filters.filter.supervisedBy.label": "Search Supervised By",
|
||||
|
||||
|
||||
|
||||
"search.filters.entityType.JournalIssue": "Journal Issue",
|
||||
|
||||
@@ -4839,12 +4898,16 @@
|
||||
|
||||
|
||||
|
||||
"supervisedWorkspace.search.results.head": "Supervised Items",
|
||||
|
||||
"workspace.search.results.head": "Your submissions",
|
||||
|
||||
"workflowAdmin.search.results.head": "Administer Workflow",
|
||||
|
||||
"workflow.search.results.head": "Workflow tasks",
|
||||
|
||||
"supervision.search.results.head": "Workflow and Workspace tasks",
|
||||
|
||||
|
||||
|
||||
"workflow-item.edit.breadcrumbs": "Edit workflowitem",
|
||||
|
Reference in New Issue
Block a user