+
+
+
+
+
{{fileName}} ({{fileData?.sizeBytes | dsFileSize}})
diff --git a/src/app/submission/sections/upload/file/section-upload-file.component.spec.ts b/src/app/submission/sections/upload/file/section-upload-file.component.spec.ts
index 4f62ceef6c6..c12991c55f2 100644
--- a/src/app/submission/sections/upload/file/section-upload-file.component.spec.ts
+++ b/src/app/submission/sections/upload/file/section-upload-file.component.spec.ts
@@ -66,7 +66,7 @@ describe('SubmissionSectionUploadFileComponent test suite', () => {
const fileName = '123456-test-upload.jpg';
const fileId = '123456-test-upload';
const fileData: any = mockUploadFiles[0];
- const pathCombiner = new JsonPatchOperationPathCombiner('sections', sectionId, 'files', fileIndex);
+ const pathCombiner = new JsonPatchOperationPathCombiner('sections', sectionId);
const jsonPatchOpBuilder: any = jasmine.createSpyObj('jsonPatchOpBuilder', {
add: jasmine.createSpy('add'),
@@ -201,6 +201,23 @@ describe('SubmissionSectionUploadFileComponent test suite', () => {
});
});
+ it('should delete primary if file we delete is primary', () => {
+ compAsAny.isPrimary = true;
+ compAsAny.pathCombiner = pathCombiner;
+ operationsService.jsonPatchByResourceID.and.returnValue(observableOf({}));
+ compAsAny.deleteFile();
+ expect(operationsBuilder.remove).toHaveBeenCalledWith(pathCombiner.getPath('primary'));
+ expect(uploadService.updateFilePrimaryBitstream).toHaveBeenCalledWith(submissionId, sectionId, null);
+ });
+
+ it('should NOT delete primary if file we delete is NOT primary', () => {
+ compAsAny.isPrimary = false;
+ compAsAny.pathCombiner = pathCombiner;
+ operationsService.jsonPatchByResourceID.and.returnValue(observableOf({}));
+ compAsAny.deleteFile();
+ expect(uploadService.updateFilePrimaryBitstream).not.toHaveBeenCalledTimes(1);
+ });
+
it('should delete file properly', () => {
compAsAny.pathCombiner = pathCombiner;
operationsService.jsonPatchByResourceID.and.returnValue(observableOf({}));
@@ -209,7 +226,8 @@ describe('SubmissionSectionUploadFileComponent test suite', () => {
compAsAny.deleteFile();
expect(uploadService.removeUploadedFile).toHaveBeenCalledWith(submissionId, sectionId, fileId);
- expect(operationsBuilder.remove).toHaveBeenCalledWith(pathCombiner.getPath());
+ expect(operationsBuilder.remove).toHaveBeenCalledWith(pathCombiner.getPath(['files', fileIndex]));
+
expect(operationsService.jsonPatchByResourceID).toHaveBeenCalledWith(
'workspaceitems',
submissionId,
diff --git a/src/app/submission/sections/upload/file/section-upload-file.component.ts b/src/app/submission/sections/upload/file/section-upload-file.component.ts
index 26fb9445cba..f95e8213ca9 100644
--- a/src/app/submission/sections/upload/file/section-upload-file.component.ts
+++ b/src/app/submission/sections/upload/file/section-upload-file.component.ts
@@ -1,5 +1,4 @@
import {
- ChangeDetectorRef,
Component,
Input,
OnChanges,
@@ -9,7 +8,7 @@ import {
ViewChild
} from '@angular/core';
-import { BehaviorSubject, Subscription } from 'rxjs';
+import { BehaviorSubject, Observable, Subscription } from 'rxjs';
import { filter } from 'rxjs/operators';
import { DynamicFormControlModel, } from '@ng-dynamic-forms/core';
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
@@ -22,7 +21,6 @@ import { JsonPatchOperationPathCombiner } from '../../../../core/json-patch/buil
import { WorkspaceitemSectionUploadFileObject } from '../../../../core/submission/models/workspaceitem-section-upload-file.model';
import { SubmissionFormsModel } from '../../../../core/config/models/config-submission-forms.model';
import { SubmissionService } from '../../../submission.service';
-import { HALEndpointService } from '../../../../core/shared/hal-endpoint.service';
import { SubmissionJsonPatchOperationsService } from '../../../../core/submission/submission-json-patch-operations.service';
import { SubmissionSectionUploadFileEditComponent } from './edit/section-upload-file-edit.component';
import { Bitstream } from '../../../../core/shared/bitstream.model';
@@ -37,6 +35,12 @@ import { NgbModalOptions } from '@ng-bootstrap/ng-bootstrap/modal/modal-config';
templateUrl: './section-upload-file.component.html',
})
export class SubmissionSectionUploadFileComponent implements OnChanges, OnInit, OnDestroy {
+ /**
+ * The indicator is the primary bitstream
+ * it will be null if no primary bitstream is set for the ORIGINAL bundle
+ * @type {boolean, null}
+ */
+ @Input() isPrimary: boolean | null;
/**
* The list of available access condition
@@ -100,6 +104,11 @@ export class SubmissionSectionUploadFileComponent implements OnChanges, OnInit,
*/
@ViewChild(SubmissionSectionUploadFileEditComponent) fileEditComp: SubmissionSectionUploadFileEditComponent;
+ /**
+ * A boolean representing if a submission save operation is pending
+ * @type {Observable
}
+ */
+ public processingSaveStatus$: Observable;
/**
* The bitstream's metadata data
@@ -137,6 +146,12 @@ export class SubmissionSectionUploadFileComponent implements OnChanges, OnInit,
*/
protected pathCombiner: JsonPatchOperationPathCombiner;
+ /**
+ * The [JsonPatchOperationPathCombiner] object
+ * @type {JsonPatchOperationPathCombiner}
+ */
+ protected primaryBitstreamPathCombiner: JsonPatchOperationPathCombiner;
+
/**
* Array to track all subscriptions and unsubscribe them onDestroy
* @type {Array}
@@ -162,9 +177,7 @@ export class SubmissionSectionUploadFileComponent implements OnChanges, OnInit,
* @param {SectionUploadService} uploadService
*/
constructor(
- private cdr: ChangeDetectorRef,
private formService: FormService,
- private halService: HALEndpointService,
private modalService: NgbModal,
private operationsBuilder: JsonPatchOperationsBuilder,
private operationsService: SubmissionJsonPatchOperationsService,
@@ -197,7 +210,8 @@ export class SubmissionSectionUploadFileComponent implements OnChanges, OnInit,
*/
ngOnInit() {
this.formId = this.formService.getUniqueId(this.fileId);
- this.pathCombiner = new JsonPatchOperationPathCombiner('sections', this.sectionId, 'files', this.fileIndex);
+ this.processingSaveStatus$ = this.submissionService.getSubmissionSaveProcessingStatus(this.submissionId);
+ this.pathCombiner = new JsonPatchOperationPathCombiner('sections', this.sectionId);
this.loadFormMetadata();
}
@@ -247,7 +261,12 @@ export class SubmissionSectionUploadFileComponent implements OnChanges, OnInit,
activeModal.componentInstance.formMetadata = this.formMetadata;
activeModal.componentInstance.pathCombiner = this.pathCombiner;
activeModal.componentInstance.submissionId = this.submissionId;
+ activeModal.componentInstance.isPrimary = this.isPrimary;
+ }
+ togglePrimaryBitstream(event) {
+ this.uploadService.updatePrimaryBitstreamOperation(this.pathCombiner.getPath('primary'), this.isPrimary, event.target.checked, this.fileId);
+ this.submissionService.dispatchSaveSection(this.submissionId, this.sectionId);
}
ngOnDestroy(): void {
@@ -273,13 +292,20 @@ export class SubmissionSectionUploadFileComponent implements OnChanges, OnInit,
* Delete bitstream from submission
*/
protected deleteFile() {
- this.operationsBuilder.remove(this.pathCombiner.getPath());
+ this.operationsBuilder.remove(this.pathCombiner.getPath(['files', this.fileIndex]));
+ if (this.isPrimary) {
+ this.operationsBuilder.remove(this.pathCombiner.getPath('primary'));
+ }
+
this.subscriptions.push(this.operationsService.jsonPatchByResourceID(
this.submissionService.getSubmissionObjectLinkName(),
this.submissionId,
this.pathCombiner.rootElement,
this.pathCombiner.subRootElement)
.subscribe(() => {
+ if (this.isPrimary) {
+ this.uploadService.updateFilePrimaryBitstream(this.submissionId, this.sectionId, null);
+ }
this.uploadService.removeUploadedFile(this.submissionId, this.sectionId, this.fileId);
this.processingDelete$.next(false);
}));
diff --git a/src/app/submission/sections/upload/file/themed-section-upload-file.component.ts b/src/app/submission/sections/upload/file/themed-section-upload-file.component.ts
index 9e0a265c3ce..952813e03c9 100644
--- a/src/app/submission/sections/upload/file/themed-section-upload-file.component.ts
+++ b/src/app/submission/sections/upload/file/themed-section-upload-file.component.ts
@@ -17,6 +17,13 @@ export class ThemedSubmissionSectionUploadFileComponent
*/
@Input() availableAccessConditionOptions: any[];
+ /**
+ * The indicator is the primary bitstream
+ * it will be null if no primary bitstream is set for the ORIGINAL bundle
+ * @type {boolean, null}
+ */
+ @Input() isPrimary: boolean | null;
+
/**
* The submission id
* @type {string}
@@ -69,6 +76,7 @@ export class ThemedSubmissionSectionUploadFileComponent
protected inAndOutputNames: (keyof SubmissionSectionUploadFileComponent & keyof this)[] = [
'availableAccessConditionOptions',
+ 'isPrimary',
'collectionId',
'collectionPolicyType',
'configMetadataForm',
diff --git a/src/app/submission/sections/upload/section-upload.component.html b/src/app/submission/sections/upload/section-upload.component.html
index b57b4542885..41e912e613a 100644
--- a/src/app/submission/sections/upload/section-upload.component.html
+++ b/src/app/submission/sections/upload/section-upload.component.html
@@ -2,15 +2,7 @@
[dismissible]="true"
[type]="AlertTypeEnum.Info">
-
-
-
-
{{'submission.sections.upload.no-file-uploaded' | translate}}
-
-
-
-
- 0">
+ 0; else noFileUploaded">
0" class="row">
@@ -26,16 +18,26 @@
{{'submission.sections.upload.n
-
-
+
+
+ {{ 'bitstream.edit.form.primaryBitstream.label' | translate }}
+
+
+
+
@@ -45,3 +47,11 @@
{{'submission.sections.upload.n
+
+
+
+
+
{{'submission.sections.upload.no-file-uploaded' | translate}}
+
+
+
diff --git a/src/app/submission/sections/upload/section-upload.component.spec.ts b/src/app/submission/sections/upload/section-upload.component.spec.ts
index 068fc5c7660..eaa510a696c 100644
--- a/src/app/submission/sections/upload/section-upload.component.spec.ts
+++ b/src/app/submission/sections/upload/section-upload.component.spec.ts
@@ -25,6 +25,7 @@ import {
mockUploadConfigResponse,
mockUploadConfigResponseNotRequired,
mockUploadFiles,
+ mockUploadFilesData,
} from '../../../shared/mocks/submission.mock';
import { SubmissionUploadsConfigDataService } from '../../../core/config/submission-uploads-config-data.service';
import { SectionUploadService } from './section-upload.service';
@@ -160,6 +161,7 @@ describe('SubmissionSectionUploadComponent test suite', () => {
);
bitstreamService.getUploadedFileList.and.returnValue(observableOf([]));
+ bitstreamService.getUploadedFilesData.and.returnValue(observableOf({ primary: null, files: [] }));
};
TestBed.configureTestingModule({
@@ -230,7 +232,7 @@ describe('SubmissionSectionUploadComponent test suite', () => {
});
it('should init component properly', () => {
-
+ bitstreamService.getUploadedFilesData.and.returnValue(observableOf({ primary: null, files: [] }));
submissionServiceStub.getSubmissionObject.and.returnValue(observableOf(submissionState));
collectionDataService.findById.and.returnValue(createSuccessfulRemoteDataObject$(Object.assign(new Collection(), mockCollection, {
@@ -246,15 +248,8 @@ describe('SubmissionSectionUploadComponent test suite', () => {
createSuccessfulRemoteDataObject$(Object.assign(new Group(), mockGroup))
);
- bitstreamService.getUploadedFileList.and.returnValue(observableOf([]));
-
comp.onSectionInit();
- const expectedGroupsMap = new Map([
- [mockUploadConfigResponse.accessConditionOptions[1].name, [mockGroup as any]],
- [mockUploadConfigResponse.accessConditionOptions[2].name, [mockGroup as any]],
- ]);
-
expect(comp.collectionId).toBe(collectionId);
expect(comp.collectionName).toBe(mockCollection.name);
expect(comp.availableAccessConditionOptions.length).toBe(4);
@@ -262,12 +257,12 @@ describe('SubmissionSectionUploadComponent test suite', () => {
expect(comp.required$.getValue()).toBe(true);
expect(compAsAny.subs.length).toBe(2);
expect(compAsAny.fileList).toEqual([]);
- expect(compAsAny.fileIndexes).toEqual([]);
expect(compAsAny.fileNames).toEqual([]);
-
+ expect(compAsAny.primaryBitstreamUUID).toEqual(null);
});
it('should init file list properly', () => {
+ bitstreamService.getUploadedFilesData.and.returnValue(observableOf({ primary: null, files: [] }));
submissionServiceStub.getSubmissionObject.and.returnValue(observableOf(submissionState));
@@ -282,7 +277,7 @@ describe('SubmissionSectionUploadComponent test suite', () => {
createSuccessfulRemoteDataObject$(Object.assign(new Group(), mockGroup))
);
- bitstreamService.getUploadedFileList.and.returnValue(observableOf(mockUploadFiles));
+ bitstreamService.getUploadedFilesData.and.returnValue(observableOf(mockUploadFilesData));
comp.onSectionInit();
@@ -298,12 +293,14 @@ describe('SubmissionSectionUploadComponent test suite', () => {
expect(comp.required$.getValue()).toBe(true);
expect(compAsAny.subs.length).toBe(2);
expect(compAsAny.fileList).toEqual(mockUploadFiles);
- expect(compAsAny.fileIndexes).toEqual(['123456-test-upload']);
+ expect(compAsAny.primaryBitstreamUUID).toEqual(null);
expect(compAsAny.fileNames).toEqual(['123456-test-upload.jpg']);
});
it('should properly read the section status when required is true', () => {
+ bitstreamService.getUploadedFilesData.and.returnValue(observableOf({ primary: null, files: [] }));
+
submissionServiceStub.getSubmissionObject.and.returnValue(observableOf(submissionState));
collectionDataService.findById.and.returnValue(createSuccessfulRemoteDataObject$(mockCollection));
@@ -324,7 +321,7 @@ describe('SubmissionSectionUploadComponent test suite', () => {
comp.onSectionInit();
- expect(comp.required$.getValue()).toBe(true);
+ expect(comp.required$.getValue()).toBe(true);
expect(compAsAny.getSectionStatus()).toBeObservable(cold('-c-d', {
c: false,
@@ -335,6 +332,7 @@ describe('SubmissionSectionUploadComponent test suite', () => {
it('should properly read the section status when required is false', () => {
submissionServiceStub.getSubmissionObject.and.returnValue(observableOf(submissionState));
+ bitstreamService.getUploadedFilesData.and.returnValue(observableOf({ primary: null, files: [] }));
collectionDataService.findById.and.returnValue(createSuccessfulRemoteDataObject$(mockCollection));
resourcePolicyService.findByHref.and.returnValue(createSuccessfulRemoteDataObject$(mockDefaultAccessCondition));
diff --git a/src/app/submission/sections/upload/section-upload.component.ts b/src/app/submission/sections/upload/section-upload.component.ts
index 10203adbc0d..ed64d514f98 100644
--- a/src/app/submission/sections/upload/section-upload.component.ts
+++ b/src/app/submission/sections/upload/section-upload.component.ts
@@ -4,7 +4,8 @@ import {
BehaviorSubject,
combineLatest as observableCombineLatest,
Observable,
- Subscription
+ Subscription,
+ combineLatest
} from 'rxjs';
import { distinctUntilChanged, filter, map, mergeMap, switchMap, tap } from 'rxjs/operators';
@@ -31,6 +32,7 @@ import { AccessConditionOption } from '../../../core/config/models/config-access
import { followLink } from '../../../shared/utils/follow-link-config.model';
import { getFirstSucceededRemoteData } from '../../../core/shared/operators';
import { DSONameService } from '../../../core/breadcrumbs/dso-name.service';
+import { WorkspaceitemSectionUploadObject } from 'src/app/core/submission/models/workspaceitem-section-upload.model';
export const POLICY_DEFAULT_NO_LIST = 1; // Banner1
export const POLICY_DEFAULT_WITH_LIST = 2; // Banner2
@@ -58,10 +60,10 @@ export class SubmissionSectionUploadComponent extends SectionModelComponent {
public AlertTypeEnum = AlertType;
/**
- * The array containing the keys of file list array
+ * The uuid of primary bitstream file
* @type {Array}
*/
- public fileIndexes: string[] = [];
+ public primaryBitstreamUUID: string | null = null;
/**
* The file list
@@ -194,27 +196,18 @@ export class SubmissionSectionUploadComponent extends SectionModelComponent {
this.changeDetectorRef.detectChanges();
}),
- // retrieve submission's bitstreams from state
- observableCombineLatest(this.configMetadataForm$,
- this.bitstreamService.getUploadedFileList(this.submissionId, this.sectionData.id)).pipe(
- filter(([configMetadataForm, fileList]: [SubmissionFormsModel, any[]]) => {
- return isNotEmpty(configMetadataForm) && isNotUndefined(fileList);
+
+ // retrieve submission's bitstream data from state
+ combineLatest([this.configMetadataForm$,
+ this.bitstreamService.getUploadedFilesData(this.submissionId, this.sectionData.id)]).pipe(
+ filter(([configMetadataForm, { files }]: [SubmissionFormsModel, WorkspaceitemSectionUploadObject]) => {
+ return isNotEmpty(configMetadataForm) && isNotEmpty(files);
}),
distinctUntilChanged())
- .subscribe(([configMetadataForm, fileList]: [SubmissionFormsModel, any[]]) => {
- this.fileList = [];
- this.fileIndexes = [];
- this.fileNames = [];
- this.changeDetectorRef.detectChanges();
- if (isNotUndefined(fileList) && fileList.length > 0) {
- fileList.forEach((file) => {
- this.fileList.push(file);
- this.fileIndexes.push(file.uuid);
- this.fileNames.push(this.getFileName(configMetadataForm, file));
- });
- }
-
- this.changeDetectorRef.detectChanges();
+ .subscribe(([configMetadataForm, { primary, files }]: [SubmissionFormsModel, WorkspaceitemSectionUploadObject]) => {
+ this.primaryBitstreamUUID = primary;
+ this.fileList = files;
+ this.fileNames = Array.from(files, file => this.getFileName(configMetadataForm, file));
}
)
);
diff --git a/src/app/submission/sections/upload/section-upload.service.spec.ts b/src/app/submission/sections/upload/section-upload.service.spec.ts
new file mode 100644
index 00000000000..e1d2abe0486
--- /dev/null
+++ b/src/app/submission/sections/upload/section-upload.service.spec.ts
@@ -0,0 +1,63 @@
+import { NO_ERRORS_SCHEMA } from '@angular/core';
+import { TestBed, waitForAsync } from '@angular/core/testing';
+import { JsonPatchOperationPathCombiner } from 'src/app/core/json-patch/builder/json-patch-operation-path-combiner';
+import { JsonPatchOperationsBuilder } from 'src/app/core/json-patch/builder/json-patch-operations-builder';
+import { SectionUploadService } from './section-upload.service';
+import { Store, StoreModule } from '@ngrx/store';
+
+const jsonPatchOpBuilder: any = jasmine.createSpyObj('jsonPatchOpBuilder', {
+ add: jasmine.createSpy('add'),
+ replace: jasmine.createSpy('replace'),
+ remove: jasmine.createSpy('remove'),
+});
+
+describe('SectionUploadService test suite', () => {
+ let sectionUploadService: SectionUploadService;
+ let operationsBuilder: any;
+ const pathCombiner = new JsonPatchOperationPathCombiner('sections', 'upload');
+ const primaryPath = pathCombiner.getPath('primary');
+ const fileId = 'test';
+ beforeEach(waitForAsync(() => {
+ TestBed.configureTestingModule({
+ imports: [StoreModule],
+ providers: [
+ { provide: Store, useValue: {} },
+ SectionUploadService,
+ { provide: JsonPatchOperationsBuilder, useValue: jsonPatchOpBuilder },
+ ],
+ schemas: [NO_ERRORS_SCHEMA]
+ });
+ }));
+
+ beforeEach(() => {
+ sectionUploadService = TestBed.inject(SectionUploadService);
+ operationsBuilder = TestBed.inject(JsonPatchOperationsBuilder);
+ });
+
+ [
+ {
+ initialPrimary: null,
+ primary: true,
+ operationName: 'add',
+ expected: [primaryPath, fileId, false, true]
+ },
+ {
+ initialPrimary: true,
+ primary: false,
+ operationName: 'remove',
+ expected: [primaryPath]
+ },
+ {
+ initialPrimary: false,
+ primary: true,
+ operationName: 'replace',
+ expected: [primaryPath, fileId, true]
+ }
+ ].forEach(({ initialPrimary, primary, operationName, expected }) => {
+ it(`updatePrimaryBitstreamOperation should add ${operationName} operation`, () => {
+ const path = pathCombiner.getPath('primary');
+ sectionUploadService.updatePrimaryBitstreamOperation(path, initialPrimary, primary, fileId);
+ expect(operationsBuilder[operationName]).toHaveBeenCalledWith(...expected);
+ });
+ });
+});
diff --git a/src/app/submission/sections/upload/section-upload.service.ts b/src/app/submission/sections/upload/section-upload.service.ts
index a851fa9dafa..ef32ea4cfdc 100644
--- a/src/app/submission/sections/upload/section-upload.service.ts
+++ b/src/app/submission/sections/upload/section-upload.service.ts
@@ -8,11 +8,15 @@ import { SubmissionState } from '../../submission.reducers';
import {
DeleteUploadedFileAction,
EditFileDataAction,
+ EditFilePrimaryBitstreamAction,
NewUploadedFileAction
} from '../../objects/submission-objects.actions';
-import { submissionUploadedFileFromUuidSelector, submissionUploadedFilesFromIdSelector } from '../../selectors';
+import { submissionSectionDataFromIdSelector, submissionUploadedFileFromUuidSelector, submissionUploadedFilesFromIdSelector } from '../../selectors';
import { isUndefined } from '../../../shared/empty.util';
import { WorkspaceitemSectionUploadFileObject } from '../../../core/submission/models/workspaceitem-section-upload-file.model';
+import { WorkspaceitemSectionUploadObject } from 'src/app/core/submission/models/workspaceitem-section-upload.model';
+import { JsonPatchOperationPathObject } from 'src/app/core/json-patch/builder/json-patch-operation-path-combiner';
+import { JsonPatchOperationsBuilder } from 'src/app/core/json-patch/builder/json-patch-operations-builder';
/**
* A service that provides methods to handle submission's bitstream state.
@@ -24,8 +28,53 @@ export class SectionUploadService {
* Initialize service variables
*
* @param {Store} store
+ * @param {JsonPatchOperationsBuilder} operationsBuilder
*/
- constructor(private store: Store) {}
+ constructor(private store: Store, private operationsBuilder: JsonPatchOperationsBuilder) {}
+
+ /**
+ * Define and add an operation based on a change
+ *
+ * @param path
+ * The path to endpoint
+ * @param intitialPrimary
+ * The initial primary indicator
+ * @param primary
+ * the new primary indicator
+ * @param fileId
+ * The file id
+ * @returns {void}
+ */
+ public updatePrimaryBitstreamOperation(path: JsonPatchOperationPathObject, intitialPrimary: boolean | null, primary: boolean | null, fileId: string): void {
+ if (intitialPrimary === null && primary) {
+ this.operationsBuilder.add(path, fileId, false, true);
+ return;
+ }
+
+ if (intitialPrimary !== primary) {
+ if (primary) {
+ this.operationsBuilder.replace(path, fileId, true);
+ return;
+ }
+ this.operationsBuilder.remove(path);
+ }
+ }
+
+ /**
+ * Return submission's bitstream data from state
+ *
+ * @param submissionId
+ * The submission id
+ * @param sectionId
+ * The section id
+ * @returns {WorkspaceitemSectionUploadObject}
+ * Returns submission's bitstream data
+ */
+ public getUploadedFilesData(submissionId: string, sectionId: string): Observable {
+ return this.store.select(submissionSectionDataFromIdSelector(submissionId, sectionId)).pipe(
+ map((state) => state),
+ distinctUntilChanged());
+ }
/**
* Return submission's bitstream list from state
@@ -104,6 +153,22 @@ export class SectionUploadService {
);
}
+ /**
+ * Update primary bitstream into the state
+ *
+ * @param submissionId
+ * The submission id
+ * @param sectionId
+ * The section id
+ * @param fileUUID
+ * The bitstream UUID
+ */
+ public updateFilePrimaryBitstream(submissionId: string, sectionId: string, fileUUID: string | null) {
+ this.store.dispatch(
+ new EditFilePrimaryBitstreamAction(submissionId, sectionId, fileUUID)
+ );
+ }
+
/**
* Update bitstream metadata into the state
*
diff --git a/src/app/suggestion-notifications/suggestions.service.ts b/src/app/suggestion-notifications/suggestions.service.ts
index 874659eab5f..3b2ce081f1d 100644
--- a/src/app/suggestion-notifications/suggestions.service.ts
+++ b/src/app/suggestion-notifications/suggestions.service.ts
@@ -11,7 +11,7 @@ import { hasValue, isNotEmpty } from '../shared/empty.util';
import { ResearcherProfile } from '../core/profile/model/researcher-profile.model';
import {
getAllSucceededRemoteDataPayload,
- getFinishedRemoteData,
+ getFinishedRemoteData, getFirstCompletedRemoteData,
getFirstSucceededRemoteDataPayload,
getFirstSucceededRemoteListPayload
} from '../core/shared/operators';
@@ -155,10 +155,10 @@ export class SuggestionsService {
*/
public retrieveCurrentUserSuggestions(userUuid: string): Observable {
return this.researcherProfileService.findById(userUuid, true).pipe(
- getFirstSucceededRemoteDataPayload(),
- mergeMap((profile: ResearcherProfile) => {
- if (isNotEmpty(profile)) {
- return this.researcherProfileService.findRelatedItemId(profile).pipe(
+ getFirstCompletedRemoteData(),
+ mergeMap((profile: RemoteData ) => {
+ if (isNotEmpty(profile) && profile.hasSucceeded && isNotEmpty(profile.payload)) {
+ return this.researcherProfileService.findRelatedItemId(profile.payload).pipe(
mergeMap((itemId: string) => {
return this.suggestionsDataService.getTargetsByUser(itemId).pipe(
getFirstSucceededRemoteListPayload()
@@ -169,7 +169,7 @@ export class SuggestionsService {
return of([]);
}
}),
- take(1)
+ catchError(() => of([]))
);
}
diff --git a/src/assets/i18n/en.json5 b/src/assets/i18n/en.json5
index 6c68cae19c5..6fa86dcb179 100644
--- a/src/assets/i18n/en.json5
+++ b/src/assets/i18n/en.json5
@@ -726,7 +726,7 @@
"bitstream.edit.form.newFormat.hint": "The application you used to create the file, and the version number (for example, \"ACMESoft SuperApp version 1.5\").",
- "bitstream.edit.form.primaryBitstream.label": "Primary bitstream",
+ "bitstream.edit.form.primaryBitstream.label": "Primary File",
"bitstream.edit.form.selectedFormat.hint": "If the format is not in the above list, select \"format not in list\" above and describe it under \"Describe new format\".",
@@ -1108,9 +1108,7 @@
"collection.logo": "Collection logo",
- "collection.page.browse.recent.head": "Recent Submissions",
-
- "collection.page.browse.recent.empty": "No items to show",
+ "collection.page.browse.search.head": "Search",
"collection.page.edit": "Edit this collection",
@@ -1120,6 +1118,8 @@
"collection.page.news": "News",
+ "collection.search.results.head": "Search Results",
+
"collection.select.confirm": "Confirm selected",
"collection.select.empty": "No collections to show",
@@ -1196,6 +1196,8 @@
"community.browse.logo": "Browse for a community logo",
+ "community.subcoms-cols.breadcrumbs": "Subcommunities and Collections",
+
"community.create.head": "Create a Community",
"community.create.notifications.success": "Successfully created the Community",
@@ -1348,6 +1350,8 @@
"community.all-lists.head": "Subcommunities and Collections",
+ "community.search.results.head": "Search Results",
+
"community.sub-collection-list.head": "Collections in this Community",
"community.sub-community-list.head": "Communities in this Community",
@@ -2496,6 +2500,8 @@
"item.page.bitstreams.collapse": "Collapse",
+ "item.page.bitstreams.primary": "Primary",
+
"item.page.filesection.original.bundle": "Original bundle",
"item.page.filesection.license.bundle": "License bundle",
@@ -4370,6 +4376,8 @@
"submission.import-external.source.datacite": "DataCite",
+ "submission.import-external.source.doi": "DOI",
+
"submission.import-external.source.scielo": "SciELO",
"submission.import-external.source.scopus": "Scopus",
@@ -4838,6 +4846,10 @@
"submission.sections.toggle.aria.close": "Collapse {{sectionHeader}} section",
+ "submission.sections.upload.primary.make": "Make {{fileName}} the primary bitstream",
+
+ "submission.sections.upload.primary.remove": "Remove {{fileName}} as the primary bitstream",
+
"submission.sections.upload.delete.confirm.cancel": "Cancel",
"submission.sections.upload.delete.confirm.info": "This operation can't be undone. Are you sure?",
diff --git a/src/config/app-config.interface.ts b/src/config/app-config.interface.ts
index 6c4b99cb0f4..51a116fa70e 100644
--- a/src/config/app-config.interface.ts
+++ b/src/config/app-config.interface.ts
@@ -8,6 +8,7 @@ import { SubmissionConfig } from './submission-config.interface';
import { FormConfig } from './form-config.interfaces';
import { LangConfig } from './lang-config.interface';
import { ItemConfig } from './item-config.interface';
+import { CommunityPageConfig } from './community-page-config.interface';
import { CollectionPageConfig } from './collection-page-config.interface';
import { ThemeConfig } from './theme.config';
import { AuthConfig } from './auth-config.interfaces';
@@ -41,6 +42,7 @@ interface AppConfig extends Config {
communityList: CommunityListConfig;
homePage: HomeConfig;
item: ItemConfig;
+ community: CommunityPageConfig;
collection: CollectionPageConfig;
themes: ThemeConfig[];
mediaViewer: MediaViewerConfig;
diff --git a/src/config/collection-page-config.interface.ts b/src/config/collection-page-config.interface.ts
index c056df66eda..6b8352e686f 100644
--- a/src/config/collection-page-config.interface.ts
+++ b/src/config/collection-page-config.interface.ts
@@ -1,7 +1,18 @@
import { Config } from './config.interface';
+/**
+ * Collection Page Config
+ */
export interface CollectionPageConfig extends Config {
+ searchSection: CollectionSearchSectionConfig;
edit: {
undoTimeout: number;
};
}
+
+/**
+ * Config related to the collection's search tab
+ */
+export interface CollectionSearchSectionConfig {
+ showSidebar: boolean;
+}
diff --git a/src/config/community-page-config.interface.ts b/src/config/community-page-config.interface.ts
new file mode 100644
index 00000000000..268f4d6a5e3
--- /dev/null
+++ b/src/config/community-page-config.interface.ts
@@ -0,0 +1,15 @@
+import { Config } from './config.interface';
+
+/**
+ * Community Page Config
+ */
+export interface CommunityPageConfig extends Config {
+ searchSection: CommunitySearchSectionConfig;
+}
+
+/**
+ * Config related to the community's search tab
+ */
+export interface CommunitySearchSectionConfig {
+ showSidebar: boolean;
+}
diff --git a/src/config/default-app-config.ts b/src/config/default-app-config.ts
index 3b3eb00ddbf..dc90f4f9511 100644
--- a/src/config/default-app-config.ts
+++ b/src/config/default-app-config.ts
@@ -23,6 +23,7 @@ import { HomeConfig } from './homepage-config.interface';
import { MarkdownConfig } from './markdown-config.interface';
import { FilterVocabularyConfig } from './filter-vocabulary-config';
import { DiscoverySortConfig } from './discovery-sort.config';
+import { CommunityPageConfig } from './community-page-config.interface';
import {QualityAssuranceConfig} from './quality-assurance.config';
export class DefaultAppConfig implements AppConfig {
@@ -291,8 +292,18 @@ export class DefaultAppConfig implements AppConfig {
}
};
+ // Community Page Config
+ community: CommunityPageConfig = {
+ searchSection: {
+ showSidebar: true,
+ },
+ };
+
// Collection Page Config
collection: CollectionPageConfig = {
+ searchSection: {
+ showSidebar: true,
+ },
edit: {
undoTimeout: 10000 // 10 seconds
}
diff --git a/src/environments/environment.test.ts b/src/environments/environment.test.ts
index afc4082dde3..02551e4c331 100644
--- a/src/environments/environment.test.ts
+++ b/src/environments/environment.test.ts
@@ -257,7 +257,15 @@ export const environment: BuildConfig = {
pageSize: 5
}
},
+ community: {
+ searchSection: {
+ showSidebar: true,
+ },
+ },
collection: {
+ searchSection: {
+ showSidebar: true,
+ },
edit: {
undoTimeout: 10000 // 10 seconds
}