{{"item.edit.bitstreams.discard-button" | translate}}
@@ -55,7 +54,7 @@
class="fas fa-undo-alt">
{{"item.edit.bitstreams.reinstate-button" | translate}}
-
{{"item.edit.bitstreams.save-button" | translate}}
diff --git a/src/app/+item-page/edit-item-page/item-bitstreams/item-bitstreams.component.ts b/src/app/+item-page/edit-item-page/item-bitstreams/item-bitstreams.component.ts
index ea269137a0..f911e67d6c 100644
--- a/src/app/+item-page/edit-item-page/item-bitstreams/item-bitstreams.component.ts
+++ b/src/app/+item-page/edit-item-page/item-bitstreams/item-bitstreams.component.ts
@@ -10,8 +10,8 @@ import { NotificationsService } from '../../../shared/notifications/notification
import { TranslateService } from '@ngx-translate/core';
import { GLOBAL_CONFIG, GlobalConfig } from '../../../../config';
import { BitstreamDataService } from '../../../core/data/bitstream-data.service';
-import { hasValue, isNotEmptyOperator } from '../../../shared/empty.util';
-import { zip as observableZip } from 'rxjs';
+import { hasValue, isNotEmpty, isNotEmptyOperator } from '../../../shared/empty.util';
+import { zip as observableZip, combineLatest as observableCombineLatest, of as observableOf } from 'rxjs';
import { ErrorResponse, RestResponse } from '../../../core/cache/response.models';
import { ObjectCacheService } from '../../../core/cache/object-cache.service';
import { RequestService } from '../../../core/data/request.service';
@@ -52,6 +52,12 @@ export class ItemBitstreamsComponent extends AbstractItemUpdateComponent impleme
pageSize: 9999
} as any;
+ /**
+ * Are we currently submitting the changes?
+ * Used to disable any action buttons until the submit finishes
+ */
+ submitting = false;
+
/**
* A subscription that checks when the item is deleted in cache and reloads the item by sending a new request
* This is used to update the item in cache after bitstreams are deleted
@@ -113,6 +119,7 @@ export class ItemBitstreamsComponent extends AbstractItemUpdateComponent impleme
).subscribe((itemRD: RemoteData- ) => {
if (hasValue(itemRD)) {
this.item = itemRD.payload;
+ this.postItemInit();
this.initializeOriginalFields();
this.initializeUpdates();
this.cdRef.detectChanges();
@@ -122,41 +129,74 @@ export class ItemBitstreamsComponent extends AbstractItemUpdateComponent impleme
/**
* Submit the current changes
+ * Bitstreams that were dragged around send out a patch request with move operations to the rest API
* Bitstreams marked as deleted send out a delete request to the rest API
* Display notifications and reset the current item/updates
*/
submit() {
- const removedBitstreams$ = this.bundles$.pipe(
- take(1),
+ this.submitting = true;
+ const bundlesOnce$ = this.bundles$.pipe(take(1));
+
+ // Fetch all move operations for each bundle
+ const moveOperations$ = bundlesOnce$.pipe(
+ switchMap((bundles: Bundle[]) => observableZip(...bundles.map((bundle: Bundle) =>
+ this.objectUpdatesService.getMoveOperations(bundle.self).pipe(
+ take(1),
+ map((operations: MoveOperation[]) => [...operations.map((operation: MoveOperation) => Object.assign(operation, {
+ from: `/_links/bitstreams${operation.from}/href`,
+ path: `/_links/bitstreams${operation.path}/href`
+ }))])
+ )
+ )))
+ );
+
+ // Send out an immediate patch request for each bundle
+ const patchResponses$ = observableCombineLatest(bundlesOnce$, moveOperations$).pipe(
+ switchMap(([bundles, moveOperationList]: [Bundle[], Operation[][]]) =>
+ observableZip(...bundles.map((bundle: Bundle, index: number) => {
+ if (isNotEmpty(moveOperationList[index])) {
+ return this.bundleService.immediatePatch(bundle, moveOperationList[index]);
+ } else {
+ return observableOf(undefined);
+ }
+ }))
+ )
+ );
+
+ // Fetch all removed bitstreams from the object update service
+ const removedBitstreams$ = bundlesOnce$.pipe(
switchMap((bundles: Bundle[]) => observableZip(
...bundles.map((bundle: Bundle) => this.objectUpdatesService.getFieldUpdates(bundle.self, [], true))
)),
map((fieldUpdates: FieldUpdates[]) => ([] as FieldUpdate[]).concat(
...fieldUpdates.map((updates: FieldUpdates) => Object.values(updates).filter((fieldUpdate: FieldUpdate) => fieldUpdate.changeType === FieldChangeType.REMOVE))
)),
- map((fieldUpdates: FieldUpdate[]) => fieldUpdates.map((fieldUpdate: FieldUpdate) => fieldUpdate.field)),
- isNotEmptyOperator()
+ map((fieldUpdates: FieldUpdate[]) => fieldUpdates.map((fieldUpdate: FieldUpdate) => fieldUpdate.field))
);
- removedBitstreams$.pipe(
+ // Send out delete requests for all deleted bitstreams
+ const removedResponses$ = removedBitstreams$.pipe(
take(1),
- switchMap((removedBistreams: Bitstream[]) => observableZip(...removedBistreams.map((bitstream: Bitstream) => this.bitstreamService.deleteAndReturnResponse(bitstream))))
- ).subscribe((responses: RestResponse[]) => {
- this.displayNotifications(responses);
- this.reset();
- });
+ switchMap((removedBistreams: Bitstream[]) => {
+ if (isNotEmpty(removedBistreams)) {
+ return observableZip(...removedBistreams.map((bitstream: Bitstream) => this.bitstreamService.deleteAndReturnResponse(bitstream)));
+ } else {
+ return observableOf(undefined);
+ }
+ })
+ );
- this.bundles$.pipe(take(1)).subscribe((bundles: Bundle[]) => {
- bundles.forEach((bundle: Bundle) => {
- this.objectUpdatesService.getMoveOperations(bundle.self).pipe(
- take(1),
- isNotEmptyOperator(),
- map((operations: MoveOperation[]) => [...operations.map((operation: MoveOperation) => Object.assign(operation, {
- from: `/_links/bitstreams${operation.from}/href`,
- path: `/_links/bitstreams${operation.path}/href`
- }))])
- ).subscribe((operations: Operation[]) => this.bundleService.patch(bundle.self, operations));
- });
+ // Perform the setup actions from above in order and display notifications
+ patchResponses$.pipe(
+ switchMap((responses: RestResponse[]) => {
+ this.displayNotifications('item.edit.bitstreams.notifications.move', responses);
+ return removedResponses$
+ }),
+ take(1)
+ ).subscribe((responses: RestResponse[]) => {
+ this.displayNotifications('item.edit.bitstreams.notifications.remove', responses);
+ this.reset();
+ this.submitting = false;
});
}
@@ -164,17 +204,20 @@ export class ItemBitstreamsComponent extends AbstractItemUpdateComponent impleme
* Display notifications
* - Error notification for each failed response with their message
* - Success notification in case there's at least one successful response
- * @param responses
+ * @param key The i18n key for the notification messages
+ * @param responses The returned responses to display notifications for
*/
- displayNotifications(responses: RestResponse[]) {
- const failedResponses = responses.filter((response: RestResponse) => !response.isSuccessful);
- const successfulResponses = responses.filter((response: RestResponse) => response.isSuccessful);
+ displayNotifications(key: string, responses: RestResponse[]) {
+ if (isNotEmpty(responses)) {
+ const failedResponses = responses.filter((response: RestResponse) => hasValue(response) && !response.isSuccessful);
+ const successfulResponses = responses.filter((response: RestResponse) => hasValue(response) && response.isSuccessful);
- failedResponses.forEach((response: ErrorResponse) => {
- this.notificationsService.error(this.getNotificationTitle('failed'), response.errorMessage);
- });
- if (successfulResponses.length > 0) {
- this.notificationsService.success(this.getNotificationTitle('saved'), this.getNotificationContent('saved'));
+ failedResponses.forEach((response: ErrorResponse) => {
+ this.notificationsService.error(this.translateService.instant(`${key}.failed.title`), response.errorMessage);
+ });
+ if (successfulResponses.length > 0) {
+ this.notificationsService.success(this.translateService.instant(`${key}.saved.title`), this.translateService.instant(`${key}.saved.content`));
+ }
}
}
@@ -230,8 +273,14 @@ export class ItemBitstreamsComponent extends AbstractItemUpdateComponent impleme
* Remove the current item's cache from object- and request-cache
*/
refreshItemCache() {
- this.objectCache.remove(this.item.self);
- this.requestService.removeByHrefSubstring(this.item.self);
+ this.bundles$.pipe(take(1)).subscribe((bundles: Bundle[]) => {
+ bundles.forEach((bundle: Bundle) => {
+ this.objectCache.remove(bundle.self);
+ this.requestService.removeByHrefSubstring(bundle.self);
+ });
+ this.objectCache.remove(this.item.self);
+ this.requestService.removeByHrefSubstring(this.item.self);
+ });
}
/**
diff --git a/src/app/+item-page/edit-item-page/item-bitstreams/item-edit-bitstream-bundle/item-edit-bitstream-bundle.component.html b/src/app/+item-page/edit-item-page/item-bitstreams/item-edit-bitstream-bundle/item-edit-bitstream-bundle.component.html
index 3c0bce24d9..f308933a00 100644
--- a/src/app/+item-page/edit-item-page/item-bitstreams/item-edit-bitstream-bundle/item-edit-bitstream-bundle.component.html
+++ b/src/app/+item-page/edit-item-page/item-bitstreams/item-edit-bitstream-bundle/item-edit-bitstream-bundle.component.html
@@ -21,7 +21,6 @@
'bg-white': updateValue.changeType === undefined
}">
diff --git a/src/app/+item-page/edit-item-page/item-bitstreams/item-edit-bitstream-bundle/item-edit-bitstream-bundle.component.ts b/src/app/+item-page/edit-item-page/item-bitstreams/item-edit-bitstream-bundle/item-edit-bitstream-bundle.component.ts
index 0db3c73cf2..e0738d39b4 100644
--- a/src/app/+item-page/edit-item-page/item-bitstreams/item-edit-bitstream-bundle/item-edit-bitstream-bundle.component.ts
+++ b/src/app/+item-page/edit-item-page/item-bitstreams/item-edit-bitstream-bundle/item-edit-bitstream-bundle.component.ts
@@ -40,11 +40,6 @@ export class ItemEditBitstreamBundleComponent implements OnInit {
*/
@Input() item: Item;
- /**
- * The current url of this page
- */
- @Input() url: string;
-
/**
* The bitstreams within this bundle retrieved from the REST API
*/
diff --git a/src/app/+item-page/edit-item-page/item-bitstreams/item-edit-bitstream/item-edit-bitstream.component.ts b/src/app/+item-page/edit-item-page/item-bitstreams/item-edit-bitstream/item-edit-bitstream.component.ts
index fb74a6261b..0b5e7ec2ae 100644
--- a/src/app/+item-page/edit-item-page/item-bitstreams/item-edit-bitstream/item-edit-bitstream.component.ts
+++ b/src/app/+item-page/edit-item-page/item-bitstreams/item-edit-bitstream/item-edit-bitstream.component.ts
@@ -28,11 +28,6 @@ export class ItemEditBitstreamComponent implements OnChanges, OnInit {
*/
@Input() fieldUpdate: FieldUpdate;
- /**
- * The current url of this page
- */
- @Input() url: string;
-
/**
* The url of the bundle
*/
diff --git a/src/app/core/data/data.service.ts b/src/app/core/data/data.service.ts
index aa6782b787..e5e9c472f7 100644
--- a/src/app/core/data/data.service.ts
+++ b/src/app/core/data/data.service.ts
@@ -17,7 +17,7 @@ import {
FindAllOptions,
FindAllRequest,
FindByIDRequest,
- GetRequest
+ GetRequest, PatchRequest
} from './request.models';
import { RequestService } from './request.service';
import { HttpOptions } from '../dspace-rest-v2/dspace-rest-v2.service';
@@ -207,6 +207,32 @@ export abstract class DataService {
this.objectCache.addPatch(href, operations);
}
+ /**
+ * Send out an immediate patch request, instead of adding to the object cache first
+ * This is useful in cases where you need the returned response and an object cache update is not needed
+ * @param dso The dso to send the patch to
+ * @param operations The patch operations
+ */
+ immediatePatch(dso: T, operations: Operation[]): Observable {
+ const requestId = this.requestService.generateRequestId();
+
+ const hrefObs = this.halService.getEndpoint(this.linkPath).pipe(
+ map((endpoint: string) => this.getIDHref(endpoint, dso.uuid)));
+
+ hrefObs.pipe(
+ find((href: string) => hasValue(href)),
+ map((href: string) => {
+ const request = new PatchRequest(requestId, href, operations);
+ this.requestService.configure(request);
+ })
+ ).subscribe();
+
+ return this.requestService.getByUUID(requestId).pipe(
+ find((request: RequestEntry) => request.completed),
+ map((request: RequestEntry) => request.response)
+ );
+ }
+
/**
* 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