diff --git a/src/app/+my-dspace-page/my-dspace-new-submission/my-dspace-new-submission.component.spec.ts b/src/app/+my-dspace-page/my-dspace-new-submission/my-dspace-new-submission.component.spec.ts
index 16b50d18f02..b0210b4979f 100644
--- a/src/app/+my-dspace-page/my-dspace-new-submission/my-dspace-new-submission.component.spec.ts
+++ b/src/app/+my-dspace-page/my-dspace-new-submission/my-dspace-new-submission.component.spec.ts
@@ -1,5 +1,5 @@
import { ChangeDetectorRef, Component, NO_ERRORS_SCHEMA } from '@angular/core';
-import { async, ComponentFixture, inject, TestBed, tick, fakeAsync } from '@angular/core/testing';
+import { async, ComponentFixture, inject, TestBed } from '@angular/core/testing';
import { RouterTestingModule } from '@angular/router/testing';
import { Store } from '@ngrx/store';
@@ -15,7 +15,6 @@ import { createTestComponent } from '../../shared/testing/utils.test';
import { MyDSpaceNewSubmissionComponent } from './my-dspace-new-submission.component';
import { AppState } from '../../app.reducer';
import { TranslateLoaderMock } from '../../shared/mocks/translate-loader.mock';
-import { getMockTranslateService } from '../../shared/mocks/translate.service.mock';
import { NotificationsService } from '../../shared/notifications/notifications.service';
import { NotificationsServiceStub } from '../../shared/testing/notifications-service.stub';
import { SharedModule } from '../../shared/shared.module';
@@ -23,10 +22,25 @@ import { getMockScrollToService } from '../../shared/mocks/scroll-to-service.moc
import { UploaderService } from '../../shared/uploader/uploader.service';
import { By } from '@angular/platform-browser';
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
+import { UploaderComponent } from 'src/app/shared/uploader/uploader.component';
describe('MyDSpaceNewSubmissionComponent test', () => {
- const translateService: any = getMockTranslateService();
+ const translateService: TranslateService = jasmine.createSpyObj('translateService', {
+ get: (key: string): any => { observableOf(key) },
+ instant: jasmine.createSpy('instant')
+ });
+
+ const uploader: any = jasmine.createSpyObj('uploader', {
+ clearQueue: jasmine.createSpy('clearQueue')
+ });
+
+ const modalService = {
+ open: () => {
+ return { result: new Promise((res, rej) => {/****/}) };
+ }
+ };
+
const store: Store
= jasmine.createSpyObj('store', {
/* tslint:disable:no-empty */
dispatch: {},
@@ -56,11 +70,7 @@ describe('MyDSpaceNewSubmissionComponent test', () => {
{ provide: ScrollToService, useValue: getMockScrollToService() },
{ provide: Store, useValue: store },
{ provide: TranslateService, useValue: translateService },
- {
- provide: NgbModal, useValue: {
- open: () => {/*comment*/}
- }
- },
+ { provide: NgbModal, useValue: modalService },
ChangeDetectorRef,
MyDSpaceNewSubmissionComponent,
UploaderService
@@ -100,6 +110,10 @@ describe('MyDSpaceNewSubmissionComponent test', () => {
beforeEach(() => {
fixture = TestBed.createComponent(MyDSpaceNewSubmissionComponent);
comp = fixture.componentInstance;
+ comp.uploadFilesOptions.authToken = 'user-auth-token';
+ comp.uploadFilesOptions.url = 'https://fake.upload-api.url';
+ comp.uploaderComponent = TestBed.createComponent(UploaderComponent).componentInstance;
+ comp.uploaderComponent.uploader = uploader;
});
it('should call app.openDialog', () => {
@@ -111,6 +125,12 @@ describe('MyDSpaceNewSubmissionComponent test', () => {
});
expect(comp.openDialog).toHaveBeenCalled();
});
+
+ it('should show a collection selector if only one file are uploaded', () => {
+ spyOn((comp as any).modalService, 'open').and.returnValue({ result: new Promise((res, rej) => {/****/}) });
+ comp.afterFileLoaded(['']);
+ expect((comp as any).modalService.open).toHaveBeenCalled();
+ });
});
});
diff --git a/src/app/+my-dspace-page/my-dspace-new-submission/my-dspace-new-submission.component.ts b/src/app/+my-dspace-page/my-dspace-new-submission/my-dspace-new-submission.component.ts
index 8d20a5736a3..f12280dafdf 100644
--- a/src/app/+my-dspace-page/my-dspace-new-submission/my-dspace-new-submission.component.ts
+++ b/src/app/+my-dspace-page/my-dspace-new-submission/my-dspace-new-submission.component.ts
@@ -1,11 +1,8 @@
-import { ChangeDetectorRef, Component, EventEmitter, OnDestroy, OnInit, Output } from '@angular/core';
+import { ChangeDetectorRef, Component, EventEmitter, OnDestroy, OnInit, Output, ViewChild } from '@angular/core';
import { Subscription } from 'rxjs';
import { first } from 'rxjs/operators';
-import { Store } from '@ngrx/store';
import { TranslateService } from '@ngx-translate/core';
-
-import { SubmissionState } from '../../submission/submission.reducers';
import { AuthService } from '../../core/auth/auth.service';
import { DSpaceObject } from '../../core/shared/dspace-object.model';
import { NotificationsService } from '../../shared/notifications/notifications.service';
@@ -15,9 +12,11 @@ import { HALEndpointService } from '../../core/shared/hal-endpoint.service';
import { NotificationType } from '../../shared/notifications/models/notification-type';
import { hasValue } from '../../shared/empty.util';
import { SearchResult } from '../../shared/search/search-result.model';
-import { Router } from '@angular/router';
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
import { CreateItemParentSelectorComponent } from 'src/app/shared/dso-selector/modal-wrappers/create-item-parent-selector/create-item-parent-selector.component';
+import { CollectionSelectorComponent } from '../collection-selector/collection-selector.component';
+import { UploaderComponent } from 'src/app/shared/uploader/uploader.component';
+import { UploaderError } from 'src/app/shared/uploader/uploader-error.model';
/**
* This component represents the whole mydspace page header
@@ -43,6 +42,11 @@ export class MyDSpaceNewSubmissionComponent implements OnDestroy, OnInit {
*/
private sub: Subscription;
+ /**
+ * Reference to uploaderComponent
+ */
+ @ViewChild(UploaderComponent, { static: false }) uploaderComponent: UploaderComponent;
+
/**
* Initialize instance variables
*
@@ -57,9 +61,7 @@ export class MyDSpaceNewSubmissionComponent implements OnDestroy, OnInit {
private changeDetectorRef: ChangeDetectorRef,
private halService: HALEndpointService,
private notificationsService: NotificationsService,
- private store: Store,
private translate: TranslateService,
- private router: Router,
private modalService: NgbModal) {
}
@@ -67,6 +69,7 @@ export class MyDSpaceNewSubmissionComponent implements OnDestroy, OnInit {
* Initialize url and Bearer token
*/
ngOnInit() {
+ this.uploadFilesOptions.autoUpload = false;
this.sub = this.halService.getEndpoint('workspaceitems').pipe(first()).subscribe((url) => {
this.uploadFilesOptions.url = url;
this.uploadFilesOptions.authToken = this.authService.buildAuthHeader();
@@ -106,8 +109,12 @@ export class MyDSpaceNewSubmissionComponent implements OnDestroy, OnInit {
/**
* Method called on file upload error
*/
- public onUploadError() {
- this.notificationsService.error(null, this.translate.get('mydspace.upload.upload-failed'));
+ public onUploadError(error: UploaderError) {
+ let errorMessageKey = 'mydspace.upload.upload-failed';
+ if (hasValue(error.status) && error.status === 422) {
+ errorMessageKey = 'mydspace.upload.upload-failed-manyentries';
+ }
+ this.notificationsService.error(null, this.translate.get(errorMessageKey));
}
/**
@@ -118,6 +125,28 @@ export class MyDSpaceNewSubmissionComponent implements OnDestroy, OnInit {
this.modalService.open(CreateItemParentSelectorComponent);
}
+ /**
+ * Method invoked after all file are loaded from upload plugin
+ */
+ afterFileLoaded(items) {
+ const uploader = this.uploaderComponent.uploader;
+ if (hasValue(items) && items.length > 1) {
+ this.notificationsService.error(null, this.translate.get('mydspace.upload.upload-failed-moreonefile'));
+ uploader.clearQueue();
+ this.changeDetectorRef.detectChanges();
+ } else {
+ const modalRef = this.modalService.open(CollectionSelectorComponent);
+ // When the dialog are closes its takes the collection selected and
+ // uploads choosed file after adds owningCollection parameter
+ modalRef.result.then( (result) => {
+ uploader.onBuildItemForm = (fileItem: any, form: any) => {
+ form.append('owningCollection', result.uuid);
+ };
+ uploader.uploadAll();
+ });
+ }
+ }
+
/**
* Unsubscribe from the subscription
*/
diff --git a/src/app/+my-dspace-page/my-dspace-page.module.ts b/src/app/+my-dspace-page/my-dspace-page.module.ts
index 1cf30c4ec95..49570fec6d7 100644
--- a/src/app/+my-dspace-page/my-dspace-page.module.ts
+++ b/src/app/+my-dspace-page/my-dspace-page.module.ts
@@ -20,6 +20,7 @@ import { SearchResultListElementComponent } from '../shared/object-list/search-r
import { ItemSearchResultListElementSubmissionComponent } from '../shared/object-list/my-dspace-result-list-element/item-search-result/item-search-result-list-element-submission.component';
import { WorkflowItemSearchResultListElementComponent } from '../shared/object-list/my-dspace-result-list-element/workflow-item-search-result/workflow-item-search-result-list-element.component';
import { PoolSearchResultDetailElementComponent } from '../shared/object-detail/my-dspace-result-detail-element/pool-search-result/pool-search-result-detail-element.component';
+import { CollectionSelectorComponent } from './collection-selector/collection-selector.component';
@NgModule({
imports: [
@@ -40,7 +41,8 @@ import { PoolSearchResultDetailElementComponent } from '../shared/object-detail/
ClaimedTaskSearchResultDetailElementComponent,
PoolSearchResultDetailElementComponent,
MyDSpaceNewSubmissionComponent,
- ItemSearchResultListElementSubmissionComponent
+ ItemSearchResultListElementSubmissionComponent,
+ CollectionSelectorComponent
],
providers: [
MyDSpaceGuard,
@@ -57,7 +59,8 @@ import { PoolSearchResultDetailElementComponent } from '../shared/object-detail/
WorkflowItemSearchResultDetailElementComponent,
ClaimedTaskSearchResultDetailElementComponent,
PoolSearchResultDetailElementComponent,
- ItemSearchResultListElementSubmissionComponent
+ ItemSearchResultListElementSubmissionComponent,
+ CollectionSelectorComponent
]
})
diff --git a/src/app/shared/uploader/uploader-error.model.ts b/src/app/shared/uploader/uploader-error.model.ts
new file mode 100644
index 00000000000..9238a0df367
--- /dev/null
+++ b/src/app/shared/uploader/uploader-error.model.ts
@@ -0,0 +1,9 @@
+/**
+ * An interface that represents the upload error values
+ */
+export interface UploaderError {
+ item?: any;
+ response?: any;
+ status?: any;
+ headers?: any;
+}
diff --git a/src/app/shared/uploader/uploader-options.model.ts b/src/app/shared/uploader/uploader-options.model.ts
index f195b0930e5..959e5c32959 100644
--- a/src/app/shared/uploader/uploader-options.model.ts
+++ b/src/app/shared/uploader/uploader-options.model.ts
@@ -17,6 +17,11 @@ export class UploaderOptions {
*/
autoUpload = true;
+ /**
+ * Set the max number of files that can be loaded
+ */
+ maxFileNumber: number;
+
/**
* The request method to use for the file upload request
*/
diff --git a/src/app/shared/uploader/uploader.component.ts b/src/app/shared/uploader/uploader.component.ts
index 72a38d1eb15..07f4245954b 100644
--- a/src/app/shared/uploader/uploader.component.ts
+++ b/src/app/shared/uploader/uploader.component.ts
@@ -69,6 +69,11 @@ export class UploaderComponent {
*/
@Output() onUploadError: EventEmitter = new EventEmitter();
+ /**
+ * The function to call when a file is selected
+ */
+ @Output() onFileSelected: EventEmitter = new EventEmitter();
+
public uploader: FileUploader;
public uploaderId: string;
public isOverBaseDropZone = observableOf(false);
@@ -102,7 +107,8 @@ export class UploaderComponent {
itemAlias: this.uploadFilesOptions.itemAlias,
removeAfterUpload: true,
autoUpload: this.uploadFilesOptions.autoUpload,
- method: this.uploadFilesOptions.method
+ method: this.uploadFilesOptions.method,
+ queueLimit: this.uploadFilesOptions.maxFileNumber
});
if (isUndefined(this.enableDragOverDocument)) {
@@ -121,6 +127,9 @@ export class UploaderComponent {
this.uploader.onAfterAddingFile = ((item) => {
item.withCredentials = false;
});
+ this.uploader.onAfterAddingAll = ((items) => {
+ this.onFileSelected.emit(items);
+ });
if (isUndefined(this.onBeforeUpload)) {
this.onBeforeUpload = () => {return};
}
@@ -149,7 +158,7 @@ export class UploaderComponent {
}
};
this.uploader.onErrorItem = (item: any, response: any, status: any, headers: any) => {
- this.onUploadError.emit(null);
+ this.onUploadError.emit({ item: item, response: response, status: status, headers: headers });
this.uploader.cancelAll();
};
this.uploader.onProgressAll = () => this.onProgress();
diff --git a/src/assets/i18n/en.json5 b/src/assets/i18n/en.json5
index bfa1c81aa69..b9c03eafc88 100644
--- a/src/assets/i18n/en.json5
+++ b/src/assets/i18n/en.json5
@@ -959,6 +959,8 @@
"dso-selector.create.item.head": "New item",
+ "dso-selector.create.submission.head": "New submission",
+
"dso-selector.edit.collection.head": "Edit collection",
"dso-selector.edit.community.head": "Edit community",
@@ -1955,6 +1957,10 @@
"mydspace.upload.upload-failed": "Error creating new workspace. Please verify the content uploaded before retry.",
+ "mydspace.upload.upload-failed-manyentries": "Unprocessable file. Detected too many entries but allowed only one for file.",
+
+ "mydspace.upload.upload-failed-moreonefile": "Unprocessable request. Only one file is allowed.",
+
"mydspace.upload.upload-multiple-successful": "{{qty}} new workspace items created.",
"mydspace.upload.upload-successful": "New workspace item created. Click {{here}} for edit it.",