mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-07 10:04:11 +00:00
234 lines
8.3 KiB
TypeScript
234 lines
8.3 KiB
TypeScript
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, HostListener, Input, Output, ViewEncapsulation, } from '@angular/core';
|
|
|
|
import { of as observableOf } from 'rxjs';
|
|
import { FileUploader } from 'ng2-file-upload';
|
|
import uniqueId from 'lodash/uniqueId';
|
|
import { ScrollToService } from '@nicky-lenaers/ngx-scroll-to';
|
|
|
|
import { UploaderOptions } from './uploader-options.model';
|
|
import { hasValue, isNotEmpty, isUndefined } from '../../empty.util';
|
|
import { UploaderProperties } from './uploader-properties.model';
|
|
import { HttpXsrfTokenExtractor } from '@angular/common/http';
|
|
import { XSRF_COOKIE, XSRF_REQUEST_HEADER, XSRF_RESPONSE_HEADER } from '../../../core/xsrf/xsrf.constants';
|
|
import { CookieService } from '../../../core/services/cookie.service';
|
|
import { DragService } from '../../../core/drag.service';
|
|
|
|
@Component({
|
|
selector: 'ds-uploader',
|
|
templateUrl: 'uploader.component.html',
|
|
styleUrls: ['uploader.component.scss'],
|
|
changeDetection: ChangeDetectionStrategy.Default,
|
|
encapsulation: ViewEncapsulation.Emulated
|
|
})
|
|
|
|
export class UploaderComponent {
|
|
|
|
/**
|
|
* The message to show when drag files on the drop zone
|
|
*/
|
|
@Input() dropMsg: string;
|
|
|
|
/**
|
|
* The message to show when drag files on the window document
|
|
*/
|
|
@Input() dropOverDocumentMsg: string;
|
|
|
|
/**
|
|
* The message to show when drag files on the window document
|
|
*/
|
|
@Input() enableDragOverDocument: boolean;
|
|
|
|
/**
|
|
* The function to call before an upload
|
|
*/
|
|
@Input() onBeforeUpload: () => void;
|
|
|
|
/**
|
|
* Configuration for the ng2-file-upload component.
|
|
*/
|
|
@Input() uploadFilesOptions: UploaderOptions;
|
|
|
|
/**
|
|
* Extra properties to be passed with the form-data of the upload
|
|
*/
|
|
@Input() uploadProperties: UploaderProperties;
|
|
|
|
/**
|
|
* The aria label to describe what kind of files need to be uploaded
|
|
*/
|
|
@Input() ariaLabel: string;
|
|
|
|
/**
|
|
* The function to call when upload is completed
|
|
*/
|
|
@Output() onCompleteItem: EventEmitter<any> = new EventEmitter<any>();
|
|
|
|
/**
|
|
* The function to call on error occurred
|
|
*/
|
|
@Output() onUploadError: EventEmitter<any> = new EventEmitter<any>();
|
|
|
|
/**
|
|
* The function to call when a file is selected
|
|
*/
|
|
@Output() onFileSelected: EventEmitter<any> = new EventEmitter<any>();
|
|
|
|
public uploader: FileUploader;
|
|
public uploaderId: string;
|
|
public isOverBaseDropZone = observableOf(false);
|
|
public isOverDocumentDropZone = observableOf(false);
|
|
|
|
@HostListener('window:dragover', ['$event'])
|
|
onDragOver(event: any) {
|
|
|
|
if (this.enableDragOverDocument && this.dragService.isAllowedDragOverPage()) {
|
|
// Show drop area on the page
|
|
event.preventDefault();
|
|
if ((event.target as any).tagName !== 'HTML') {
|
|
this.isOverDocumentDropZone = observableOf(true);
|
|
}
|
|
}
|
|
}
|
|
|
|
constructor(
|
|
private cdr: ChangeDetectorRef,
|
|
private scrollToService: ScrollToService,
|
|
private dragService: DragService,
|
|
private tokenExtractor: HttpXsrfTokenExtractor,
|
|
private cookieService: CookieService
|
|
) {
|
|
}
|
|
|
|
/**
|
|
* Method provided by Angular. Invoked after the constructor.
|
|
*/
|
|
ngOnInit() {
|
|
this.uploaderId = 'ds-drag-and-drop-uploader' + uniqueId();
|
|
this.checkConfig(this.uploadFilesOptions);
|
|
this.uploader = new FileUploader({
|
|
url: this.uploadFilesOptions.url,
|
|
authToken: this.uploadFilesOptions.authToken,
|
|
disableMultipart: this.uploadFilesOptions.disableMultipart,
|
|
itemAlias: this.uploadFilesOptions.itemAlias,
|
|
removeAfterUpload: true,
|
|
autoUpload: this.uploadFilesOptions.autoUpload,
|
|
method: this.uploadFilesOptions.method,
|
|
queueLimit: this.uploadFilesOptions.maxFileNumber,
|
|
});
|
|
|
|
if (isUndefined(this.enableDragOverDocument)) {
|
|
this.enableDragOverDocument = false;
|
|
}
|
|
if (isUndefined(this.dropMsg)) {
|
|
this.dropMsg = 'uploader.drag-message';
|
|
}
|
|
if (isUndefined(this.dropOverDocumentMsg)) {
|
|
this.dropOverDocumentMsg = 'uploader.drag-message';
|
|
}
|
|
}
|
|
|
|
ngAfterViewInit() {
|
|
this.uploader.onAfterAddingAll = ((items) => {
|
|
this.onFileSelected.emit(items);
|
|
});
|
|
if (isUndefined(this.onBeforeUpload)) {
|
|
this.onBeforeUpload = () => {return;};
|
|
}
|
|
this.uploader.onBeforeUploadItem = (item) => {
|
|
if (item.url !== this.uploader.options.url) {
|
|
item.url = this.uploader.options.url;
|
|
}
|
|
// Ensure the current XSRF token is included in every upload request (token may change between items uploaded)
|
|
this.uploader.options.headers = [{ name: XSRF_REQUEST_HEADER, value: this.tokenExtractor.getToken() }];
|
|
this.onBeforeUpload();
|
|
this.isOverDocumentDropZone = observableOf(false);
|
|
};
|
|
if (hasValue(this.uploadProperties)) {
|
|
this.uploader.onBuildItemForm = (item, form) => {
|
|
form.append('properties', JSON.stringify(this.uploadProperties));
|
|
};
|
|
}
|
|
this.uploader.onCompleteItem = (item: any, response: any, status: any, headers: any) => {
|
|
// Check for a changed XSRF token in response & save new token if found (to both cookie & header for next request)
|
|
// NOTE: this is only necessary because ng2-file-upload doesn't use an Http service and therefore never
|
|
// triggers our xsrf.interceptor.ts. See this bug: https://github.com/valor-software/ng2-file-upload/issues/950
|
|
const token = headers[XSRF_RESPONSE_HEADER.toLowerCase()];
|
|
if (isNotEmpty(token)) {
|
|
this.saveXsrfToken(token);
|
|
this.uploader.options.headers = [{ name: XSRF_REQUEST_HEADER, value: this.tokenExtractor.getToken() }];
|
|
}
|
|
|
|
if (isNotEmpty(response)) {
|
|
const responsePath = JSON.parse(response);
|
|
this.onCompleteItem.emit(responsePath);
|
|
}
|
|
};
|
|
this.uploader.onErrorItem = (item: any, response: any, status: any, headers: any) => {
|
|
// Check for a changed XSRF token in response & save new token if found (to both cookie & header for next request)
|
|
// NOTE: this is only necessary because ng2-file-upload doesn't use an Http service and therefore never
|
|
// triggers our xsrf.interceptor.ts. See this bug: https://github.com/valor-software/ng2-file-upload/issues/950
|
|
const token = headers[XSRF_RESPONSE_HEADER.toLowerCase()];
|
|
if (isNotEmpty(token)) {
|
|
this.saveXsrfToken(token);
|
|
this.uploader.options.headers = [{ name: XSRF_REQUEST_HEADER, value: this.tokenExtractor.getToken() }];
|
|
}
|
|
|
|
this.onUploadError.emit({ item: item, response: response, status: status, headers: headers });
|
|
this.uploader.cancelAll();
|
|
};
|
|
this.uploader.onProgressAll = () => this.onProgress();
|
|
this.uploader.onProgressItem = () => this.onProgress();
|
|
}
|
|
|
|
/**
|
|
* Called when files are dragged on the base drop area.
|
|
*/
|
|
public fileOverBase(isOver: boolean): void {
|
|
this.isOverBaseDropZone = observableOf(isOver);
|
|
}
|
|
|
|
/**
|
|
* Called when files are dragged on the window document drop area.
|
|
*/
|
|
public fileOverDocument(isOver: boolean) {
|
|
if (!isOver) {
|
|
this.isOverDocumentDropZone = observableOf(isOver);
|
|
}
|
|
}
|
|
|
|
private onProgress() {
|
|
this.cdr.detectChanges();
|
|
}
|
|
|
|
/**
|
|
* Ensure options passed contains the required properties.
|
|
*
|
|
* @param fileUploadOptions
|
|
* The upload-files options object.
|
|
*/
|
|
private checkConfig(fileUploadOptions: any) {
|
|
const required = ['url', 'authToken', 'disableMultipart', 'itemAlias'];
|
|
const missing = required.filter((prop) => {
|
|
return !((prop in fileUploadOptions) && fileUploadOptions[prop] !== '');
|
|
});
|
|
if (0 < missing.length) {
|
|
throw new Error('UploadFiles: Argument is missing the following required properties: ' + missing.join(', '));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Save XSRF token found in response. This is a temporary copy of the method in xsrf.interceptor.ts
|
|
* It can be removed once ng2-file-upload supports interceptors (see https://github.com/valor-software/ng2-file-upload/issues/950),
|
|
* or we switch to a new upload library (see https://github.com/DSpace/dspace-angular/issues/820)
|
|
* @param token token found
|
|
*/
|
|
private saveXsrfToken(token: string) {
|
|
// Save token value as a *new* value of our client-side XSRF-TOKEN cookie.
|
|
// This is the cookie that is parsed by Angular's tokenExtractor(),
|
|
// which we will send back in the X-XSRF-TOKEN header per Angular best practices.
|
|
this.cookieService.remove(XSRF_COOKIE);
|
|
this.cookieService.set(XSRF_COOKIE, token);
|
|
}
|
|
|
|
}
|