mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-07 18:14:17 +00:00
taskid 66074 Keep virtual metadata on relationship delete
This commit is contained in:
@@ -1686,5 +1686,6 @@
|
|||||||
|
|
||||||
"uploader.queue-length": "Queue length",
|
"uploader.queue-length": "Queue length",
|
||||||
|
|
||||||
|
"virtual-metadata-modal.head": "Select the items for which you want to save the virtual metadata as real metadata",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -21,6 +21,7 @@ import { ItemRelationshipsComponent } from './item-relationships/item-relationsh
|
|||||||
import { EditRelationshipComponent } from './item-relationships/edit-relationship/edit-relationship.component';
|
import { EditRelationshipComponent } from './item-relationships/edit-relationship/edit-relationship.component';
|
||||||
import { EditRelationshipListComponent } from './item-relationships/edit-relationship-list/edit-relationship-list.component';
|
import { EditRelationshipListComponent } from './item-relationships/edit-relationship-list/edit-relationship-list.component';
|
||||||
import { ItemMoveComponent } from './item-move/item-move.component';
|
import { ItemMoveComponent } from './item-move/item-move.component';
|
||||||
|
import {VirtualMetadataComponent} from "./virtual-metadata/virtual-metadata.component";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Module that contains all components related to the Edit Item page administrator functionality
|
* Module that contains all components related to the Edit Item page administrator functionality
|
||||||
@@ -51,6 +52,7 @@ import { ItemMoveComponent } from './item-move/item-move.component';
|
|||||||
EditRelationshipListComponent,
|
EditRelationshipListComponent,
|
||||||
ItemCollectionMapperComponent,
|
ItemCollectionMapperComponent,
|
||||||
ItemMoveComponent,
|
ItemMoveComponent,
|
||||||
|
VirtualMetadataComponent,
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
export class EditItemPageModule {
|
export class EditItemPageModule {
|
||||||
|
@@ -7,6 +7,7 @@
|
|||||||
class="relationship-row d-block"
|
class="relationship-row d-block"
|
||||||
[fieldUpdate]="updateValue || {}"
|
[fieldUpdate]="updateValue || {}"
|
||||||
[url]="url"
|
[url]="url"
|
||||||
|
[editItem]="item"
|
||||||
[ngClass]="{'alert alert-danger': updateValue.changeType === 2}">
|
[ngClass]="{'alert alert-danger': updateValue.changeType === 2}">
|
||||||
</div>
|
</div>
|
||||||
<ds-loading *ngIf="updateValues.length == 0" message="{{'loading.items' | translate}}"></ds-loading>
|
<ds-loading *ngIf="updateValues.length == 0" message="{{'loading.items' | translate}}"></ds-loading>
|
||||||
|
@@ -6,8 +6,7 @@ import { RelationshipService } from '../../../../core/data/relationship.service'
|
|||||||
import { Item } from '../../../../core/shared/item.model';
|
import { Item } from '../../../../core/shared/item.model';
|
||||||
import { map, switchMap} from 'rxjs/operators';
|
import { map, switchMap} from 'rxjs/operators';
|
||||||
import { hasValue } from '../../../../shared/empty.util';
|
import { hasValue } from '../../../../shared/empty.util';
|
||||||
import { RemoteData } from '../../../../core/data/remote-data';
|
import {Relationship} from "../../../../core/shared/item-relationships/relationship.model";
|
||||||
import { PaginatedList } from '../../../../core/data/paginated-list';
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-edit-relationship-list',
|
selector: 'ds-edit-relationship-list',
|
||||||
@@ -61,22 +60,17 @@ export class EditRelationshipListComponent implements OnInit, OnChanges {
|
|||||||
this.updates$ = this.getUpdatesByLabel(this.relationshipLabel);
|
this.updates$ = this.getUpdatesByLabel(this.relationshipLabel);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Transform the item's relationships of a specific type into related items
|
|
||||||
* @param label The relationship type's label
|
|
||||||
*/
|
|
||||||
public getRelatedItemsByLabel(label: string): Observable<RemoteData<PaginatedList<Item>>> {
|
|
||||||
return this.relationshipService.getRelatedItemsByLabel(this.item, label);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get FieldUpdates for the relationships of a specific type
|
* Get FieldUpdates for the relationships of a specific type
|
||||||
* @param label The relationship type's label
|
* @param label The relationship type's label
|
||||||
*/
|
*/
|
||||||
public getUpdatesByLabel(label: string): Observable<FieldUpdates> {
|
public getUpdatesByLabel(label: string): Observable<FieldUpdates> {
|
||||||
return this.getRelatedItemsByLabel(label).pipe(
|
return this.relationshipService.getItemRelationshipsByLabel(this.item, label).pipe(
|
||||||
switchMap((itemsRD) => this.objectUpdatesService.getFieldUpdatesExclusive(this.url, itemsRD.payload.page))
|
map(relationsRD => relationsRD.payload.page.map(relationship =>
|
||||||
)
|
Object.assign(new Relationship(), relationship, {uuid: relationship.id})
|
||||||
|
)),
|
||||||
|
switchMap((initialFields) => this.objectUpdatesService.getFieldUpdatesExclusive(this.url, initialFields)),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -97,5 +91,4 @@ export class EditRelationshipListComponent implements OnInit, OnChanges {
|
|||||||
trackUpdate(index, update: FieldUpdate) {
|
trackUpdate(index, update: FieldUpdate) {
|
||||||
return update && update.field ? update.field.uuid : undefined;
|
return update && update.field ? update.field.uuid : undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -1,10 +1,10 @@
|
|||||||
<div class="row" *ngIf="item">
|
<div class="row" *ngIf="relatedItem$ | async">
|
||||||
<div class="col-10 relationship">
|
<div class="col-10 relationship">
|
||||||
<ds-listable-object-component-loader [object]="item" [viewMode]="viewMode"></ds-listable-object-component-loader>
|
<ds-listable-object-component-loader [object]="relatedItem$ | async" [viewMode]="viewMode"></ds-listable-object-component-loader>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-2">
|
<div class="col-2">
|
||||||
<div class="btn-group relationship-action-buttons">
|
<div class="btn-group relationship-action-buttons">
|
||||||
<button [disabled]="!canRemove()" (click)="remove()"
|
<button [disabled]="!canRemove()" (click)="openVirtualMetadataModal(virtualMetadataModal)"
|
||||||
class="btn btn-outline-danger btn-sm"
|
class="btn btn-outline-danger btn-sm"
|
||||||
title="{{'item.edit.metadata.edit.buttons.remove' | translate}}">
|
title="{{'item.edit.metadata.edit.buttons.remove' | translate}}">
|
||||||
<i class="fas fa-trash-alt fa-fw"></i>
|
<i class="fas fa-trash-alt fa-fw"></i>
|
||||||
@@ -17,3 +17,12 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<ng-template #virtualMetadataModal>
|
||||||
|
<ds-virtual-metadata
|
||||||
|
[relationship]="relationship"
|
||||||
|
[url]="url"
|
||||||
|
(close)="closeVirtualMetadataModal()"
|
||||||
|
(save)="remove()"
|
||||||
|
>
|
||||||
|
</ds-virtual-metadata>
|
||||||
|
</ng-template>
|
||||||
|
@@ -112,7 +112,7 @@ describe('EditRelationshipComponent', () => {
|
|||||||
|
|
||||||
comp.url = url;
|
comp.url = url;
|
||||||
comp.fieldUpdate = fieldUpdate1;
|
comp.fieldUpdate = fieldUpdate1;
|
||||||
comp.item = item;
|
comp.editItem = item;
|
||||||
|
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
||||||
|
@@ -1,10 +1,15 @@
|
|||||||
import { Component, Input, OnChanges } from '@angular/core';
|
import {Component, Input, OnChanges, OnInit} from '@angular/core';
|
||||||
import { FieldUpdate } from '../../../../core/data/object-updates/object-updates.reducer';
|
import {combineLatest as observableCombineLatest, Observable} from 'rxjs';
|
||||||
import { cloneDeep } from 'lodash';
|
import {filter, map, switchMap, take, tap} from 'rxjs/operators';
|
||||||
import { Item } from '../../../../core/shared/item.model';
|
|
||||||
import { ObjectUpdatesService } from '../../../../core/data/object-updates/object-updates.service';
|
|
||||||
import {FieldChangeType} from '../../../../core/data/object-updates/object-updates.actions';
|
import {FieldChangeType} from '../../../../core/data/object-updates/object-updates.actions';
|
||||||
|
import {DeleteRelationship, FieldUpdate} from '../../../../core/data/object-updates/object-updates.reducer';
|
||||||
|
import {ObjectUpdatesService} from '../../../../core/data/object-updates/object-updates.service';
|
||||||
|
import {Relationship} from '../../../../core/shared/item-relationships/relationship.model';
|
||||||
|
import {Item} from '../../../../core/shared/item.model';
|
||||||
|
import {getRemoteDataPayload, getSucceededRemoteData} from '../../../../core/shared/operators';
|
||||||
import {ViewMode} from '../../../../core/shared/view-mode.model';
|
import {ViewMode} from '../../../../core/shared/view-mode.model';
|
||||||
|
import {hasValue, isNotEmpty} from '../../../../shared/empty.util';
|
||||||
|
import {NgbModal, NgbModalRef} from "@ng-bootstrap/ng-bootstrap";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
// tslint:disable-next-line:component-selector
|
// tslint:disable-next-line:component-selector
|
||||||
@@ -23,38 +28,109 @@ export class EditRelationshipComponent implements OnChanges {
|
|||||||
*/
|
*/
|
||||||
@Input() url: string;
|
@Input() url: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The item being edited
|
||||||
|
*/
|
||||||
|
@Input() editItem: Item;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The relationship being edited
|
||||||
|
*/
|
||||||
|
get relationship(): Relationship {
|
||||||
|
return this.fieldUpdate.field as Relationship;
|
||||||
|
}
|
||||||
|
|
||||||
|
private leftItem$: Observable<Item>;
|
||||||
|
private rightItem$: Observable<Item>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The related item of this relationship
|
* The related item of this relationship
|
||||||
*/
|
*/
|
||||||
item: Item;
|
relatedItem$: Observable<Item>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The view-mode we're currently on
|
* The view-mode we're currently on
|
||||||
*/
|
*/
|
||||||
viewMode = ViewMode.ListElement;
|
viewMode = ViewMode.ListElement;
|
||||||
|
|
||||||
constructor(private objectUpdatesService: ObjectUpdatesService) {
|
constructor(
|
||||||
|
private objectUpdatesService: ObjectUpdatesService,
|
||||||
|
private modalService: NgbModal,
|
||||||
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the current relationship based on the fieldUpdate input field
|
* Sets the current relationship based on the fieldUpdate input field
|
||||||
*/
|
*/
|
||||||
ngOnChanges(): void {
|
ngOnChanges(): void {
|
||||||
this.item = cloneDeep(this.fieldUpdate.field) as Item;
|
this.leftItem$ = this.relationship.leftItem.pipe(
|
||||||
|
getSucceededRemoteData(),
|
||||||
|
getRemoteDataPayload(),
|
||||||
|
filter((item: Item) => hasValue(item) && isNotEmpty(item.uuid))
|
||||||
|
);
|
||||||
|
this.rightItem$ = this.relationship.rightItem.pipe(
|
||||||
|
getSucceededRemoteData(),
|
||||||
|
getRemoteDataPayload(),
|
||||||
|
filter((item: Item) => hasValue(item) && isNotEmpty(item.uuid))
|
||||||
|
);
|
||||||
|
this.relatedItem$ = observableCombineLatest(
|
||||||
|
this.leftItem$,
|
||||||
|
this.rightItem$,
|
||||||
|
).pipe(
|
||||||
|
map((items: Item[]) =>
|
||||||
|
items.find((item) => item.uuid !== this.editItem.uuid)
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sends a new remove update for this field to the object updates service
|
* Sends a new remove update for this field to the object updates service
|
||||||
*/
|
*/
|
||||||
remove(): void {
|
remove(): void {
|
||||||
this.objectUpdatesService.saveRemoveFieldUpdate(this.url, this.item);
|
this.closeVirtualMetadataModal();
|
||||||
|
observableCombineLatest(
|
||||||
|
this.leftItem$,
|
||||||
|
this.rightItem$,
|
||||||
|
).pipe(
|
||||||
|
map((items: Item[]) =>
|
||||||
|
items.map(item => this.objectUpdatesService
|
||||||
|
.isSelectedVirtualMetadataItem(this.url, this.relationship.id, item.uuid))
|
||||||
|
),
|
||||||
|
switchMap((selection$: Observable<boolean>[]) => observableCombineLatest(selection$)),
|
||||||
|
map((selection: boolean[]) => {
|
||||||
|
return Object.assign({},
|
||||||
|
this.fieldUpdate.field,
|
||||||
|
{
|
||||||
|
uuid: this.relationship.id,
|
||||||
|
keepLeftVirtualMetadata: selection[0] == true,
|
||||||
|
keepRightVirtualMetadata: selection[1] == true,
|
||||||
|
}
|
||||||
|
) as DeleteRelationship
|
||||||
|
}),
|
||||||
|
take(1),
|
||||||
|
).subscribe((deleteRelationship: DeleteRelationship) =>
|
||||||
|
this.objectUpdatesService.saveRemoveFieldUpdate(this.url, deleteRelationship)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reference to NgbModal
|
||||||
|
*/
|
||||||
|
public modalRef: NgbModalRef;
|
||||||
|
|
||||||
|
openVirtualMetadataModal(content: any) {
|
||||||
|
this.modalRef = this.modalService.open(content);
|
||||||
|
}
|
||||||
|
|
||||||
|
closeVirtualMetadataModal() {
|
||||||
|
this.modalRef.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cancels the current update for this field in the object updates service
|
* Cancels the current update for this field in the object updates service
|
||||||
*/
|
*/
|
||||||
undo(): void {
|
undo(): void {
|
||||||
this.objectUpdatesService.removeSingleFieldUpdate(this.url, this.item.uuid);
|
this.objectUpdatesService.removeSingleFieldUpdate(this.url, this.fieldUpdate.field.uuid);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -70,5 +146,4 @@ export class EditRelationshipComponent implements OnChanges {
|
|||||||
canUndo(): boolean {
|
canUndo(): boolean {
|
||||||
return this.fieldUpdate.changeType >= 0;
|
return this.fieldUpdate.changeType >= 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -1,6 +1,10 @@
|
|||||||
import { ChangeDetectorRef, Component, Inject, OnDestroy } from '@angular/core';
|
import { ChangeDetectorRef, Component, Inject, OnDestroy } from '@angular/core';
|
||||||
import { Item } from '../../../core/shared/item.model';
|
import { Item } from '../../../core/shared/item.model';
|
||||||
import { FieldUpdate, FieldUpdates } from '../../../core/data/object-updates/object-updates.reducer';
|
import {
|
||||||
|
DeleteRelationship,
|
||||||
|
FieldUpdate,
|
||||||
|
FieldUpdates
|
||||||
|
} from '../../../core/data/object-updates/object-updates.reducer';
|
||||||
import { Observable } from 'rxjs/internal/Observable';
|
import { Observable } from 'rxjs/internal/Observable';
|
||||||
import {filter, map, switchMap, take, tap} from 'rxjs/operators';
|
import {filter, map, switchMap, take, tap} from 'rxjs/operators';
|
||||||
import { combineLatest as observableCombineLatest, zip as observableZip } from 'rxjs';
|
import { combineLatest as observableCombineLatest, zip as observableZip } from 'rxjs';
|
||||||
@@ -104,22 +108,36 @@ export class ItemRelationshipsComponent extends AbstractItemUpdateComponent impl
|
|||||||
* Make sure the lists are refreshed afterwards and notifications are sent for success and errors
|
* Make sure the lists are refreshed afterwards and notifications are sent for success and errors
|
||||||
*/
|
*/
|
||||||
public submit(): void {
|
public submit(): void {
|
||||||
// Get all IDs of related items of which their relationship with the current item is about to be removed
|
|
||||||
const removedItemIds$ = this.relationshipService.getRelatedItems(this.item).pipe(
|
|
||||||
switchMap((items: Item[]) => this.objectUpdatesService.getFieldUpdatesExclusive(this.url, items) as Observable<FieldUpdates>),
|
|
||||||
map((fieldUpdates: FieldUpdates) => Object.values(fieldUpdates).filter((fieldUpdate: FieldUpdate) => fieldUpdate.changeType === FieldChangeType.REMOVE)),
|
|
||||||
map((fieldUpdates: FieldUpdate[]) => fieldUpdates.map((fieldUpdate: FieldUpdate) => fieldUpdate.field.uuid) as string[]),
|
|
||||||
isNotEmptyOperator()
|
|
||||||
);
|
|
||||||
// Get all the relationships that should be removed
|
// Get all the relationships that should be removed
|
||||||
const removedRelationships$ = removedItemIds$.pipe(
|
const removedRelationshipIDs$ = this.relationshipService.getItemRelationshipsArray(this.item).pipe(
|
||||||
getRelationsByRelatedItemIds(this.item, this.relationshipService)
|
map((relationships: Relationship[]) => relationships.map(relationship =>
|
||||||
|
Object.assign(new Relationship(), relationship, {uuid: relationship.id})
|
||||||
|
)),
|
||||||
|
switchMap((relationships: Relationship[]) => {
|
||||||
|
return this.objectUpdatesService.getFieldUpdatesExclusive(this.url, relationships) as Observable<FieldUpdates>
|
||||||
|
}),
|
||||||
|
map((fieldUpdates: FieldUpdates) => Object.values(fieldUpdates).filter((fieldUpdate: FieldUpdate) => fieldUpdate.changeType === FieldChangeType.REMOVE)),
|
||||||
|
map((fieldUpdates: FieldUpdate[]) => fieldUpdates.map((fieldUpdate: FieldUpdate) => fieldUpdate.field) as DeleteRelationship[]),
|
||||||
|
isNotEmptyOperator(),
|
||||||
);
|
);
|
||||||
// Request a delete for every relationship found in the observable created above
|
removedRelationshipIDs$.pipe(
|
||||||
removedRelationships$.pipe(
|
|
||||||
take(1),
|
take(1),
|
||||||
map((removedRelationships: Relationship[]) => removedRelationships.map((rel: Relationship) => rel.id)),
|
switchMap((deleteRelationships: DeleteRelationship[]) =>
|
||||||
switchMap((removedIds: string[]) => observableZip(...removedIds.map((uuid: string) => this.relationshipService.deleteRelationship(uuid))))
|
observableZip(...deleteRelationships.map((deleteRelationship) => {
|
||||||
|
let copyVirtualMetadata : string;
|
||||||
|
if (deleteRelationship.keepLeftVirtualMetadata && deleteRelationship.keepRightVirtualMetadata) {
|
||||||
|
copyVirtualMetadata = 'all';
|
||||||
|
} else if (deleteRelationship.keepLeftVirtualMetadata) {
|
||||||
|
copyVirtualMetadata = 'left';
|
||||||
|
} else if (deleteRelationship.keepRightVirtualMetadata) {
|
||||||
|
copyVirtualMetadata = 'right';
|
||||||
|
} else {
|
||||||
|
copyVirtualMetadata = 'none';
|
||||||
|
}
|
||||||
|
return this.relationshipService.deleteRelationship(deleteRelationship.uuid, copyVirtualMetadata);
|
||||||
|
}
|
||||||
|
))
|
||||||
|
),
|
||||||
).subscribe((responses: RestResponse[]) => {
|
).subscribe((responses: RestResponse[]) => {
|
||||||
this.displayNotifications(responses);
|
this.displayNotifications(responses);
|
||||||
this.reset();
|
this.reset();
|
||||||
|
@@ -0,0 +1,42 @@
|
|||||||
|
<div>
|
||||||
|
<div class="modal-header">{{'virtual-metadata-modal.head' | translate}}
|
||||||
|
<button type="button" class="close" (click)="close.emit()" aria-label="Close">
|
||||||
|
<span aria-hidden="true">×</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<div *ngFor="let item$ of [leftItem$, rightItem$]">
|
||||||
|
<div *ngVar="item$ | async as item">
|
||||||
|
<div *ngVar="(isSelectedVirtualMetadataItem(item) | async) as selected"
|
||||||
|
(click)="setSelectedVirtualMetadataItem(item, !selected)"
|
||||||
|
class="d-flex flex-row">
|
||||||
|
<div class="m-2">
|
||||||
|
<label>
|
||||||
|
<input type="checkbox" [checked]="selected">
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="flex-column">
|
||||||
|
<ds-listable-object-component-loader
|
||||||
|
[object]="item$ | async"></ds-listable-object-component-loader>
|
||||||
|
<div *ngFor="let metadata of getVirtualMetadata(relationship, item$ | async)">
|
||||||
|
<div>
|
||||||
|
<div class="font-weight-bold">
|
||||||
|
{{metadata.metadataField}}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
{{metadata.metadataValue.value}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="d-flex flex-row-reverse m-2">
|
||||||
|
<button class="btn btn-primary"
|
||||||
|
(click)="save.emit()"><i
|
||||||
|
class="fas fa-save"></i> {{"item.edit.metadata.save-button" | translate}}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
@@ -0,0 +1,172 @@
|
|||||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
import { Item } from '../../../core/shared/item.model';
|
||||||
|
import { RouterStub } from '../../../shared/testing/router-stub';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
import { RouterTestingModule } from '@angular/router/testing';
|
||||||
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
|
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
|
||||||
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
|
import { VirtualMetadataComponent } from './virtual-metadata.component';
|
||||||
|
import { NotificationsServiceStub } from '../../../shared/testing/notifications-service-stub';
|
||||||
|
import { NotificationsService } from '../../../shared/notifications/notifications.service';
|
||||||
|
import { SearchService } from '../../../+search-page/search-service/search.service';
|
||||||
|
import { of as observableOf } from 'rxjs';
|
||||||
|
import { FormsModule } from '@angular/forms';
|
||||||
|
import { ItemDataService } from '../../../core/data/item-data.service';
|
||||||
|
import { RemoteData } from '../../../core/data/remote-data';
|
||||||
|
import { PaginatedList } from '../../../core/data/paginated-list';
|
||||||
|
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
|
||||||
|
import { RestResponse } from '../../../core/cache/response.models';
|
||||||
|
import { Collection } from '../../../core/shared/collection.model';
|
||||||
|
|
||||||
|
// describe('ItemMoveComponent', () => {
|
||||||
|
// let comp: VirtualMetadataComponent;
|
||||||
|
// let fixture: ComponentFixture<VirtualMetadataComponent>;
|
||||||
|
//
|
||||||
|
// const mockItem = Object.assign(new Item(), {
|
||||||
|
// id: 'fake-id',
|
||||||
|
// handle: 'fake/handle',
|
||||||
|
// lastModified: '2018'
|
||||||
|
// });
|
||||||
|
//
|
||||||
|
// const itemPageUrl = `fake-url/${mockItem.id}`;
|
||||||
|
// const routerStub = Object.assign(new RouterStub(), {
|
||||||
|
// url: `${itemPageUrl}/edit`
|
||||||
|
// });
|
||||||
|
//
|
||||||
|
// const mockItemDataService = jasmine.createSpyObj({
|
||||||
|
// moveToCollection: observableOf(new RestResponse(true, 200, 'Success'))
|
||||||
|
// });
|
||||||
|
//
|
||||||
|
// const mockItemDataServiceFail = jasmine.createSpyObj({
|
||||||
|
// moveToCollection: observableOf(new RestResponse(false, 500, 'Internal server error'))
|
||||||
|
// });
|
||||||
|
//
|
||||||
|
// const routeStub = {
|
||||||
|
// data: observableOf({
|
||||||
|
// item: new RemoteData(false, false, true, null, {
|
||||||
|
// id: 'item1'
|
||||||
|
// })
|
||||||
|
// })
|
||||||
|
// };
|
||||||
|
//
|
||||||
|
// const collection1 = Object.assign(new Collection(),{
|
||||||
|
// uuid: 'collection-uuid-1',
|
||||||
|
// name: 'Test collection 1',
|
||||||
|
// self: 'self-link-1',
|
||||||
|
// });
|
||||||
|
//
|
||||||
|
// const collection2 = Object.assign(new Collection(),{
|
||||||
|
// uuid: 'collection-uuid-2',
|
||||||
|
// name: 'Test collection 2',
|
||||||
|
// self: 'self-link-2',
|
||||||
|
// });
|
||||||
|
//
|
||||||
|
// const mockSearchService = {
|
||||||
|
// search: () => {
|
||||||
|
// return observableOf(new RemoteData(false, false, true, null,
|
||||||
|
// new PaginatedList(null, [
|
||||||
|
// {
|
||||||
|
// indexableObject: collection1,
|
||||||
|
// hitHighlights: {}
|
||||||
|
// }, {
|
||||||
|
// indexableObject: collection2,
|
||||||
|
// hitHighlights: {}
|
||||||
|
// }
|
||||||
|
// ])));
|
||||||
|
// }
|
||||||
|
// };
|
||||||
|
//
|
||||||
|
// const notificationsServiceStub = new NotificationsServiceStub();
|
||||||
|
//
|
||||||
|
// describe('ItemMoveComponent success', () => {
|
||||||
|
// beforeEach(async(() => {
|
||||||
|
// TestBed.configureTestingModule({
|
||||||
|
// imports: [CommonModule, FormsModule, RouterTestingModule.withRoutes([]), TranslateModule.forRoot(), NgbModule.forRoot()],
|
||||||
|
// declarations: [VirtualMetadataComponent],
|
||||||
|
// providers: [
|
||||||
|
// {provide: ActivatedRoute, useValue: routeStub},
|
||||||
|
// {provide: Router, useValue: routerStub},
|
||||||
|
// {provide: ItemDataService, useValue: mockItemDataService},
|
||||||
|
// {provide: NotificationsService, useValue: notificationsServiceStub},
|
||||||
|
// {provide: SearchService, useValue: mockSearchService},
|
||||||
|
// ], schemas: [
|
||||||
|
// CUSTOM_ELEMENTS_SCHEMA
|
||||||
|
// ]
|
||||||
|
// }).compileComponents();
|
||||||
|
// }));
|
||||||
|
//
|
||||||
|
// beforeEach(() => {
|
||||||
|
// fixture = TestBed.createComponent(VirtualMetadataComponent);
|
||||||
|
// comp = fixture.componentInstance;
|
||||||
|
// fixture.detectChanges();
|
||||||
|
// });
|
||||||
|
// it('should load suggestions', () => {
|
||||||
|
// const expected = [
|
||||||
|
// collection1,
|
||||||
|
// collection2
|
||||||
|
// ];
|
||||||
|
//
|
||||||
|
// comp.collectionSearchResults.subscribe((value) => {
|
||||||
|
// expect(value).toEqual(expected);
|
||||||
|
// }
|
||||||
|
// );
|
||||||
|
// });
|
||||||
|
// it('should get current url ', () => {
|
||||||
|
// expect(comp.getCurrentUrl()).toEqual('fake-url/fake-id/edit');
|
||||||
|
// });
|
||||||
|
// it('should on click select the correct collection name and id', () => {
|
||||||
|
// const data = collection1;
|
||||||
|
//
|
||||||
|
// comp.onClick(data);
|
||||||
|
//
|
||||||
|
// expect(comp.selectedCollectionName).toEqual('Test collection 1');
|
||||||
|
// expect(comp.selectedCollection).toEqual(collection1);
|
||||||
|
// });
|
||||||
|
// describe('moveCollection', () => {
|
||||||
|
// it('should call itemDataService.moveToCollection', () => {
|
||||||
|
// comp.itemId = 'item-id';
|
||||||
|
// comp.selectedCollectionName = 'selected-collection-id';
|
||||||
|
// comp.selectedCollection = collection1;
|
||||||
|
// comp.moveCollection();
|
||||||
|
//
|
||||||
|
// expect(mockItemDataService.moveToCollection).toHaveBeenCalledWith('item-id', collection1);
|
||||||
|
// });
|
||||||
|
// it('should call notificationsService success message on success', () => {
|
||||||
|
// comp.moveCollection();
|
||||||
|
//
|
||||||
|
// expect(notificationsServiceStub.success).toHaveBeenCalled();
|
||||||
|
// });
|
||||||
|
// });
|
||||||
|
// });
|
||||||
|
//
|
||||||
|
// describe('ItemMoveComponent fail', () => {
|
||||||
|
// beforeEach(async(() => {
|
||||||
|
// TestBed.configureTestingModule({
|
||||||
|
// imports: [CommonModule, FormsModule, RouterTestingModule.withRoutes([]), TranslateModule.forRoot(), NgbModule.forRoot()],
|
||||||
|
// declarations: [VirtualMetadataComponent],
|
||||||
|
// providers: [
|
||||||
|
// {provide: ActivatedRoute, useValue: routeStub},
|
||||||
|
// {provide: Router, useValue: routerStub},
|
||||||
|
// {provide: ItemDataService, useValue: mockItemDataServiceFail},
|
||||||
|
// {provide: NotificationsService, useValue: notificationsServiceStub},
|
||||||
|
// {provide: SearchService, useValue: mockSearchService},
|
||||||
|
// ], schemas: [
|
||||||
|
// CUSTOM_ELEMENTS_SCHEMA
|
||||||
|
// ]
|
||||||
|
// }).compileComponents();
|
||||||
|
// }));
|
||||||
|
//
|
||||||
|
// beforeEach(() => {
|
||||||
|
// fixture = TestBed.createComponent(VirtualMetadataComponent);
|
||||||
|
// comp = fixture.componentInstance;
|
||||||
|
// fixture.detectChanges();
|
||||||
|
// });
|
||||||
|
//
|
||||||
|
// it('should call notificationsService error message on fail', () => {
|
||||||
|
// comp.moveCollection();
|
||||||
|
//
|
||||||
|
// expect(notificationsServiceStub.error).toHaveBeenCalled();
|
||||||
|
// });
|
||||||
|
// });
|
||||||
|
// });
|
@@ -0,0 +1,66 @@
|
|||||||
|
import {Component, EventEmitter, Input, OnChanges, Output} from '@angular/core';
|
||||||
|
import {ActivatedRoute} from '@angular/router';
|
||||||
|
import {Observable} from 'rxjs';
|
||||||
|
import {Item} from "../../../core/shared/item.model";
|
||||||
|
import {Relationship} from "../../../core/shared/item-relationships/relationship.model";
|
||||||
|
import {MetadataValue} from "../../../core/shared/metadata.models";
|
||||||
|
import {getRemoteDataPayload, getSucceededRemoteData} from "../../../core/shared/operators";
|
||||||
|
import {ObjectUpdatesService} from "../../../core/data/object-updates/object-updates.service";
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'ds-virtual-metadata',
|
||||||
|
templateUrl: './virtual-metadata.component.html'
|
||||||
|
})
|
||||||
|
/**
|
||||||
|
* Component that handles the moving of an item to a different collection
|
||||||
|
*/
|
||||||
|
export class VirtualMetadataComponent implements OnChanges {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The current url of this page
|
||||||
|
*/
|
||||||
|
@Input() url: string;
|
||||||
|
|
||||||
|
@Input() relationship: Relationship;
|
||||||
|
|
||||||
|
@Output() close = new EventEmitter();
|
||||||
|
@Output() save = new EventEmitter();
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
protected route: ActivatedRoute,
|
||||||
|
protected objectUpdatesService: ObjectUpdatesService,
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
leftItem$: Observable<Item>;
|
||||||
|
rightItem$: Observable<Item>;
|
||||||
|
|
||||||
|
ngOnChanges(): void {
|
||||||
|
this.leftItem$ = this.relationship.leftItem.pipe(
|
||||||
|
getSucceededRemoteData(),
|
||||||
|
getRemoteDataPayload(),
|
||||||
|
);
|
||||||
|
this.rightItem$ = this.relationship.rightItem.pipe(
|
||||||
|
getSucceededRemoteData(),
|
||||||
|
getRemoteDataPayload(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
getVirtualMetadata(relationship: Relationship, relatedItem: Item): VirtualMetadata[] {
|
||||||
|
|
||||||
|
return this.objectUpdatesService.getVirtualMetadataList(relationship, relatedItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
setSelectedVirtualMetadataItem(item: Item, selected: boolean) {
|
||||||
|
this.objectUpdatesService.setSelectedVirtualMetadataItem(this.url, this.relationship.id, item.uuid, selected);
|
||||||
|
}
|
||||||
|
|
||||||
|
isSelectedVirtualMetadataItem(item: Item): Observable<boolean> {
|
||||||
|
return this.objectUpdatesService.isSelectedVirtualMetadataItem(this.url, this.relationship.id, item.uuid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface VirtualMetadata {
|
||||||
|
metadataField: string,
|
||||||
|
metadataValue: MetadataValue,
|
||||||
|
}
|
@@ -11,6 +11,7 @@ export const ObjectUpdatesActionTypes = {
|
|||||||
SET_EDITABLE_FIELD: type('dspace/core/cache/object-updates/SET_EDITABLE_FIELD'),
|
SET_EDITABLE_FIELD: type('dspace/core/cache/object-updates/SET_EDITABLE_FIELD'),
|
||||||
SET_VALID_FIELD: type('dspace/core/cache/object-updates/SET_VALID_FIELD'),
|
SET_VALID_FIELD: type('dspace/core/cache/object-updates/SET_VALID_FIELD'),
|
||||||
ADD_FIELD: type('dspace/core/cache/object-updates/ADD_FIELD'),
|
ADD_FIELD: type('dspace/core/cache/object-updates/ADD_FIELD'),
|
||||||
|
SELECT_VIRTUAL_METADATA: type('dspace/core/cache/object-updates/SELECT_VIRTUAL_METADATA'),
|
||||||
DISCARD: type('dspace/core/cache/object-updates/DISCARD'),
|
DISCARD: type('dspace/core/cache/object-updates/DISCARD'),
|
||||||
REINSTATE: type('dspace/core/cache/object-updates/REINSTATE'),
|
REINSTATE: type('dspace/core/cache/object-updates/REINSTATE'),
|
||||||
REMOVE: type('dspace/core/cache/object-updates/REMOVE'),
|
REMOVE: type('dspace/core/cache/object-updates/REMOVE'),
|
||||||
@@ -83,6 +84,34 @@ export class AddFieldUpdateAction implements Action {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class SelectVirtualMetadataAction implements Action {
|
||||||
|
|
||||||
|
type = ObjectUpdatesActionTypes.SELECT_VIRTUAL_METADATA;
|
||||||
|
payload: {
|
||||||
|
url: string,
|
||||||
|
source: string,
|
||||||
|
uuid: string,
|
||||||
|
select: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new AddFieldUpdateAction
|
||||||
|
*
|
||||||
|
* @param url
|
||||||
|
* the unique url of the page for which a field update is added
|
||||||
|
* @param field The identifiable field of which a new update is added
|
||||||
|
* @param changeType The update's change type
|
||||||
|
*/
|
||||||
|
constructor(
|
||||||
|
url: string,
|
||||||
|
source: string,
|
||||||
|
uuid: string,
|
||||||
|
select: boolean,
|
||||||
|
) {
|
||||||
|
this.payload = { url, source, uuid, select: select};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An ngrx action to set the editable state of an existing field in the ObjectUpdates state for a certain page url
|
* An ngrx action to set the editable state of an existing field in the ObjectUpdates state for a certain page url
|
||||||
*/
|
*/
|
||||||
@@ -242,4 +271,5 @@ export type ObjectUpdatesAction
|
|||||||
| DiscardObjectUpdatesAction
|
| DiscardObjectUpdatesAction
|
||||||
| ReinstateObjectUpdatesAction
|
| ReinstateObjectUpdatesAction
|
||||||
| RemoveObjectUpdatesAction
|
| RemoveObjectUpdatesAction
|
||||||
| RemoveFieldUpdateAction;
|
| RemoveFieldUpdateAction
|
||||||
|
| SelectVirtualMetadataAction;
|
||||||
|
@@ -79,7 +79,8 @@ describe('objectUpdatesReducer', () => {
|
|||||||
changeType: FieldChangeType.ADD
|
changeType: FieldChangeType.ADD
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
lastModified: modDate
|
lastModified: modDate,
|
||||||
|
virtualMetadataSources: {},
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -213,6 +214,7 @@ describe('objectUpdatesReducer', () => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
fieldUpdates: {},
|
fieldUpdates: {},
|
||||||
|
virtualMetadataSources: {},
|
||||||
lastModified: modDate
|
lastModified: modDate
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@@ -7,9 +7,13 @@ import {
|
|||||||
ObjectUpdatesActionTypes,
|
ObjectUpdatesActionTypes,
|
||||||
ReinstateObjectUpdatesAction,
|
ReinstateObjectUpdatesAction,
|
||||||
RemoveFieldUpdateAction,
|
RemoveFieldUpdateAction,
|
||||||
RemoveObjectUpdatesAction, SetEditableFieldUpdateAction, SetValidFieldUpdateAction
|
RemoveObjectUpdatesAction,
|
||||||
|
SetEditableFieldUpdateAction,
|
||||||
|
SetValidFieldUpdateAction,
|
||||||
|
SelectVirtualMetadataAction,
|
||||||
} from './object-updates.actions';
|
} from './object-updates.actions';
|
||||||
import { hasNoValue, hasValue } from '../../../shared/empty.util';
|
import { hasNoValue, hasValue } from '../../../shared/empty.util';
|
||||||
|
import {Relationship} from "../../shared/item-relationships/relationship.model";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Path where discarded objects are saved
|
* Path where discarded objects are saved
|
||||||
@@ -54,12 +58,26 @@ export interface FieldUpdates {
|
|||||||
[uuid: string]: FieldUpdate;
|
[uuid: string]: FieldUpdate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface VirtualMetadataSources {
|
||||||
|
[source: string]: VirtualMetadataSource
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface VirtualMetadataSource {
|
||||||
|
[uuid: string]: boolean,
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DeleteRelationship extends Relationship {
|
||||||
|
keepLeftVirtualMetadata: boolean,
|
||||||
|
keepRightVirtualMetadata: boolean,
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The updated state of a single page
|
* The updated state of a single page
|
||||||
*/
|
*/
|
||||||
export interface ObjectUpdatesEntry {
|
export interface ObjectUpdatesEntry {
|
||||||
fieldStates: FieldStates;
|
fieldStates: FieldStates;
|
||||||
fieldUpdates: FieldUpdates
|
fieldUpdates: FieldUpdates;
|
||||||
|
virtualMetadataSources: VirtualMetadataSources;
|
||||||
lastModified: Date;
|
lastModified: Date;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -96,6 +114,9 @@ export function objectUpdatesReducer(state = initialState, action: ObjectUpdates
|
|||||||
case ObjectUpdatesActionTypes.ADD_FIELD: {
|
case ObjectUpdatesActionTypes.ADD_FIELD: {
|
||||||
return addFieldUpdate(state, action as AddFieldUpdateAction);
|
return addFieldUpdate(state, action as AddFieldUpdateAction);
|
||||||
}
|
}
|
||||||
|
case ObjectUpdatesActionTypes.SELECT_VIRTUAL_METADATA: {
|
||||||
|
return selectVirtualMetadata(state, action as SelectVirtualMetadataAction);
|
||||||
|
}
|
||||||
case ObjectUpdatesActionTypes.DISCARD: {
|
case ObjectUpdatesActionTypes.DISCARD: {
|
||||||
return discardObjectUpdates(state, action as DiscardObjectUpdatesAction);
|
return discardObjectUpdates(state, action as DiscardObjectUpdatesAction);
|
||||||
}
|
}
|
||||||
@@ -135,6 +156,7 @@ function initializeFieldsUpdate(state: any, action: InitializeFieldsAction) {
|
|||||||
state[url],
|
state[url],
|
||||||
{ fieldStates: fieldStates },
|
{ fieldStates: fieldStates },
|
||||||
{ fieldUpdates: {} },
|
{ fieldUpdates: {} },
|
||||||
|
{ virtualMetadataSources: {} },
|
||||||
{ lastModified: lastModifiedServer }
|
{ lastModified: lastModifiedServer }
|
||||||
);
|
);
|
||||||
return Object.assign({}, state, { [url]: newPageState });
|
return Object.assign({}, state, { [url]: newPageState });
|
||||||
@@ -169,6 +191,51 @@ function addFieldUpdate(state: any, action: AddFieldUpdateAction) {
|
|||||||
return Object.assign({}, state, { [url]: newPageState });
|
return Object.assign({}, state, { [url]: newPageState });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a new update for a specific field to the store
|
||||||
|
* @param state The current state
|
||||||
|
* @param action The action to perform on the current state
|
||||||
|
*/
|
||||||
|
function selectVirtualMetadata(state: any, action: SelectVirtualMetadataAction) {
|
||||||
|
|
||||||
|
const url: string = action.payload.url;
|
||||||
|
const source: string = action.payload.source;
|
||||||
|
const uuid: string = action.payload.uuid;
|
||||||
|
const select: boolean = action.payload.select;
|
||||||
|
|
||||||
|
const pageState: ObjectUpdatesEntry = state[url] || {};
|
||||||
|
|
||||||
|
const virtualMetadataSource = Object.assign(
|
||||||
|
{},
|
||||||
|
pageState.virtualMetadataSources[source],
|
||||||
|
{
|
||||||
|
[uuid]: select,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
const virtualMetadataSources = Object.assign(
|
||||||
|
{},
|
||||||
|
pageState.virtualMetadataSources,
|
||||||
|
{
|
||||||
|
[source]: virtualMetadataSource,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
const newPageState = Object.assign(
|
||||||
|
{},
|
||||||
|
pageState,
|
||||||
|
{virtualMetadataSources: virtualMetadataSources},
|
||||||
|
);
|
||||||
|
|
||||||
|
return Object.assign(
|
||||||
|
{},
|
||||||
|
state,
|
||||||
|
{
|
||||||
|
[url]: newPageState,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Discard all updates for a specific action's url in the store
|
* Discard all updates for a specific action's url in the store
|
||||||
* @param state The current state
|
* @param state The current state
|
||||||
|
@@ -8,7 +8,8 @@ import {
|
|||||||
Identifiable,
|
Identifiable,
|
||||||
OBJECT_UPDATES_TRASH_PATH,
|
OBJECT_UPDATES_TRASH_PATH,
|
||||||
ObjectUpdatesEntry,
|
ObjectUpdatesEntry,
|
||||||
ObjectUpdatesState
|
ObjectUpdatesState,
|
||||||
|
VirtualMetadataSource
|
||||||
} from './object-updates.reducer';
|
} from './object-updates.reducer';
|
||||||
import {Observable} from 'rxjs';
|
import {Observable} from 'rxjs';
|
||||||
import {
|
import {
|
||||||
@@ -18,12 +19,17 @@ import {
|
|||||||
InitializeFieldsAction,
|
InitializeFieldsAction,
|
||||||
ReinstateObjectUpdatesAction,
|
ReinstateObjectUpdatesAction,
|
||||||
RemoveFieldUpdateAction,
|
RemoveFieldUpdateAction,
|
||||||
|
SelectVirtualMetadataAction,
|
||||||
SetEditableFieldUpdateAction,
|
SetEditableFieldUpdateAction,
|
||||||
SetValidFieldUpdateAction
|
SetValidFieldUpdateAction
|
||||||
} from './object-updates.actions';
|
} from './object-updates.actions';
|
||||||
import {distinctUntilChanged, filter, map} from 'rxjs/operators';
|
import {distinctUntilChanged, filter, map} from 'rxjs/operators';
|
||||||
import {hasNoValue, hasValue, isEmpty, isNotEmpty} from '../../../shared/empty.util';
|
import {hasNoValue, hasValue, isEmpty, isNotEmpty} from '../../../shared/empty.util';
|
||||||
import {INotification} from '../../../shared/notifications/models/notification.model';
|
import {INotification} from '../../../shared/notifications/models/notification.model';
|
||||||
|
import {Item} from "../../shared/item.model";
|
||||||
|
import {Relationship} from "../../shared/item-relationships/relationship.model";
|
||||||
|
import {MetadataValue} from "../../shared/metadata.models";
|
||||||
|
import {VirtualMetadata} from "../../../+item-page/edit-item-page/virtual-metadata/virtual-metadata.component";
|
||||||
|
|
||||||
function objectUpdatesStateSelector(): MemoizedSelector<CoreState, ObjectUpdatesState> {
|
function objectUpdatesStateSelector(): MemoizedSelector<CoreState, ObjectUpdatesState> {
|
||||||
return createSelector(coreSelector, (state: CoreState) => state['cache/object-updates']);
|
return createSelector(coreSelector, (state: CoreState) => state['cache/object-updates']);
|
||||||
@@ -37,6 +43,10 @@ function filterByUrlAndUUIDFieldStateSelector(url: string, uuid: string): Memoiz
|
|||||||
return createSelector(filterByUrlObjectUpdatesStateSelector(url), (state: ObjectUpdatesEntry) => state.fieldStates[uuid]);
|
return createSelector(filterByUrlObjectUpdatesStateSelector(url), (state: ObjectUpdatesEntry) => state.fieldStates[uuid]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function virtualMetadataSourceSelector(url: string, source: string): MemoizedSelector<CoreState, VirtualMetadataSource> {
|
||||||
|
return createSelector(filterByUrlObjectUpdatesStateSelector(url), (state: ObjectUpdatesEntry) => state.virtualMetadataSources[source]);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Service that dispatches and reads from the ObjectUpdates' state in the store
|
* Service that dispatches and reads from the ObjectUpdates' state in the store
|
||||||
*/
|
*/
|
||||||
@@ -195,6 +205,41 @@ export class ObjectUpdatesService {
|
|||||||
this.saveFieldUpdate(url, field, FieldChangeType.UPDATE);
|
this.saveFieldUpdate(url, field, FieldChangeType.UPDATE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getVirtualMetadataList(relationship: Relationship, item: Item): VirtualMetadata[] {
|
||||||
|
return Object.entries(item.metadata)
|
||||||
|
.map(([key, value]) =>
|
||||||
|
value
|
||||||
|
.filter((metadata: MetadataValue) =>
|
||||||
|
metadata.authority && metadata.authority.endsWith(relationship.id))
|
||||||
|
.map((metadata: MetadataValue) => {
|
||||||
|
return {
|
||||||
|
metadataField: key,
|
||||||
|
metadataValue: metadata,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.reduce((previous, current) => previous.concat(current));
|
||||||
|
}
|
||||||
|
|
||||||
|
isSelectedVirtualMetadataItem(url: string, relationship: string, item: string): Observable<boolean> {
|
||||||
|
|
||||||
|
return this.store
|
||||||
|
.pipe(
|
||||||
|
select(virtualMetadataSourceSelector(url, relationship)),
|
||||||
|
map(virtualMetadataSource => virtualMetadataSource && virtualMetadataSource[item]),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method to dispatch an AddFieldUpdateAction to the store
|
||||||
|
* @param url The page's URL for which the changes are saved
|
||||||
|
* @param field An updated field for the page's object
|
||||||
|
* @param changeType The last type of change applied to this field
|
||||||
|
*/
|
||||||
|
setSelectedVirtualMetadataItem(url: string, relationship: string, item: string, selected: boolean) {
|
||||||
|
this.store.dispatch(new SelectVirtualMetadataAction(url, relationship, item, selected));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Dispatches a SetEditableFieldUpdateAction to the store to set a field's editable state
|
* Dispatches a SetEditableFieldUpdateAction to the store to set a field's editable state
|
||||||
* @param url The URL of the page on which the field resides
|
* @param url The URL of the page on which the field resides
|
||||||
|
@@ -109,7 +109,7 @@ describe('RelationshipService', () => {
|
|||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
spyOn(service, 'findById').and.returnValue(getRemotedataObservable(relationship1));
|
spyOn(service, 'findById').and.returnValue(getRemotedataObservable(relationship1));
|
||||||
spyOn(objectCache, 'remove');
|
spyOn(objectCache, 'remove');
|
||||||
service.deleteRelationship(relationships[0].uuid).subscribe();
|
service.deleteRelationship(relationships[0].uuid, 'none').subscribe();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should send a DeleteRequest', () => {
|
it('should send a DeleteRequest', () => {
|
||||||
|
@@ -83,11 +83,13 @@ export class RelationshipService extends DataService<Relationship> {
|
|||||||
* Send a delete request for a relationship by ID
|
* Send a delete request for a relationship by ID
|
||||||
* @param uuid
|
* @param uuid
|
||||||
*/
|
*/
|
||||||
deleteRelationship(uuid: string): Observable<RestResponse> {
|
deleteRelationship(uuid: string, copyVirtualMetadata: string): Observable<RestResponse> {
|
||||||
return this.getRelationshipEndpoint(uuid).pipe(
|
return this.getRelationshipEndpoint(uuid).pipe(
|
||||||
isNotEmptyOperator(),
|
isNotEmptyOperator(),
|
||||||
distinctUntilChanged(),
|
distinctUntilChanged(),
|
||||||
map((endpointURL: string) => new DeleteRequest(this.requestService.generateRequestId(), endpointURL)),
|
map((endpointURL: string) =>
|
||||||
|
new DeleteRequest(this.requestService.generateRequestId(), endpointURL + "?copyVirtualMetadata=" + copyVirtualMetadata)
|
||||||
|
),
|
||||||
configureRequest(this.requestService),
|
configureRequest(this.requestService),
|
||||||
switchMap((restRequest: RestRequest) => this.requestService.getByUUID(restRequest.uuid)),
|
switchMap((restRequest: RestRequest) => this.requestService.getByUUID(restRequest.uuid)),
|
||||||
getResponseFromEntry(),
|
getResponseFromEntry(),
|
||||||
@@ -269,5 +271,4 @@ export class RelationshipService extends DataService<Relationship> {
|
|||||||
this.requestService.removeByHrefSubstring(rightItem.payload.self);
|
this.requestService.removeByHrefSubstring(rightItem.payload.self);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user