mirror of
https://github.com/Alfresco/alfresco-ng2-components.git
synced 2025-08-07 17:48:54 +00:00
[ADF-643] upload enhancements (#1949)
* rework folder uploading - flatterns hierarchy on folder upload - performs a single traversal for the entire folder heirarchy and ends with a comple file list - allows now dropping folders on existing folders - overall code improvements * fix unit tests * readme updates * clean old and unused code * code cleanup * limit concurrent uploads * update code as per review * fix upload button for Safari * fixes for Safari - Safari compatibility - code updates based on review * fix code * fix unit tests
This commit is contained in:
committed by
Eugenio Romano
parent
e7a1f46ac8
commit
a02ba4ad71
@@ -17,14 +17,12 @@
|
||||
|
||||
import { Component, ElementRef, Input, Output, EventEmitter, OnInit, OnChanges, SimpleChanges } from '@angular/core';
|
||||
import { Observable, Subject } from 'rxjs/Rx';
|
||||
import { AlfrescoApiService, AlfrescoContentService, AlfrescoTranslationService, LogService, NotificationService, AlfrescoSettingsService } from 'ng2-alfresco-core';
|
||||
import { AlfrescoApiService, AlfrescoContentService, AlfrescoTranslationService, LogService, NotificationService, AlfrescoSettingsService, FileUtils } from 'ng2-alfresco-core';
|
||||
import { MinimalNodeEntryEntity } from 'alfresco-js-api';
|
||||
import { UploadService } from '../services/upload.service';
|
||||
import { FileModel } from '../models/file.model';
|
||||
import { PermissionModel } from '../models/permissions.model';
|
||||
|
||||
const ERROR_FOLDER_ALREADY_EXIST = 409;
|
||||
|
||||
@Component({
|
||||
selector: 'alfresco-upload-button',
|
||||
templateUrl: './upload-button.component.html',
|
||||
@@ -32,11 +30,15 @@ const ERROR_FOLDER_ALREADY_EXIST = 409;
|
||||
})
|
||||
export class UploadButtonComponent implements OnInit, OnChanges {
|
||||
|
||||
private static DEFAULT_ROOT_ID: string = '-root-';
|
||||
|
||||
@Input()
|
||||
disabled: boolean = false;
|
||||
|
||||
/**
|
||||
* @deprecated Deprecated in 1.6.0, you can use UploadService events and NotificationService api instead.
|
||||
*
|
||||
* @type {boolean}
|
||||
* @memberof UploadButtonComponent
|
||||
*/
|
||||
@Input()
|
||||
showNotificationBar: boolean = true;
|
||||
|
||||
@@ -55,11 +57,17 @@ export class UploadButtonComponent implements OnInit, OnChanges {
|
||||
@Input()
|
||||
staticTitle: string;
|
||||
|
||||
/**
|
||||
* @deprecated Deprecated in 1.6.0, this property is not used for couple of releases already.
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof UploadDragAreaComponent
|
||||
*/
|
||||
@Input()
|
||||
currentFolderPath: string = '/';
|
||||
|
||||
@Input()
|
||||
rootFolderId: string = UploadButtonComponent.DEFAULT_ROOT_ID;
|
||||
rootFolderId: string = '-root-';
|
||||
|
||||
@Input()
|
||||
disableWithNoPermission: boolean = false;
|
||||
@@ -122,16 +130,11 @@ export class UploadButtonComponent implements OnInit, OnChanges {
|
||||
return !this.hasPermission && this.disableWithNoPermission ? true : undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method called when files are dropped in the drag area.
|
||||
*
|
||||
* @param {File[]} files - files dropped in the drag area.
|
||||
*/
|
||||
onFilesAdded($event: any): void {
|
||||
let files: File[] = this.getFiles($event.currentTarget.files);
|
||||
let files: File[] = FileUtils.toFileArray($event.currentTarget.files);
|
||||
|
||||
if (this.hasPermission) {
|
||||
this.uploadFiles(this.currentFolderPath, files);
|
||||
this.uploadFiles(files);
|
||||
} else {
|
||||
this.permissionEvent.emit(new PermissionModel({type: 'content', action: 'upload', permission: 'create'}));
|
||||
}
|
||||
@@ -139,38 +142,10 @@ export class UploadButtonComponent implements OnInit, OnChanges {
|
||||
$event.target.value = '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Method called when a folder is dropped in the drag area.
|
||||
*
|
||||
* @param {File[]} files - files of a folder dropped in the drag area.
|
||||
*/
|
||||
onDirectoryAdded($event: any): void {
|
||||
let files: File[] = this.getFiles($event.currentTarget.files);
|
||||
if (this.hasPermission) {
|
||||
let hashMapDir = this.convertIntoHashMap(files);
|
||||
|
||||
hashMapDir.forEach((filesDir, directoryPath) => {
|
||||
let directoryName = this.getDirectoryName(directoryPath);
|
||||
let absolutePath = this.currentFolderPath + this.getDirectoryPath(directoryPath);
|
||||
|
||||
this.contentService.createFolder(absolutePath, directoryName, this.rootFolderId)
|
||||
.subscribe(
|
||||
_ => {
|
||||
let relativeDir = this.currentFolderPath + '/' + directoryPath;
|
||||
this.uploadFiles(relativeDir, filesDir);
|
||||
},
|
||||
error => {
|
||||
let errorMessagePlaceholder = this.getErrorMessage(error.response);
|
||||
if (errorMessagePlaceholder) {
|
||||
this.onError.emit({value: errorMessagePlaceholder});
|
||||
let errorMessage = this.formatString(errorMessagePlaceholder, [directoryName]);
|
||||
if (errorMessage) {
|
||||
this.showErrorNotificationBar(errorMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
||||
let files: File[] = FileUtils.toFileArray($event.currentTarget.files);
|
||||
this.uploadFiles(files);
|
||||
} else {
|
||||
this.permissionEvent.emit(new PermissionModel({type: 'content', action: 'upload', permission: 'create'}));
|
||||
}
|
||||
@@ -180,78 +155,24 @@ export class UploadButtonComponent implements OnInit, OnChanges {
|
||||
|
||||
/**
|
||||
* Upload a list of file in the specified path
|
||||
* @param path
|
||||
* @param files
|
||||
* @param path
|
||||
*/
|
||||
uploadFiles(path: string, files: File[]): void {
|
||||
if (files.length) {
|
||||
const latestFilesAdded = files.map(f => new FileModel(f, { newVersion: this.versioning }));
|
||||
uploadFiles(files: File[]): void {
|
||||
if (files.length > 0) {
|
||||
const latestFilesAdded = files.map(file => new FileModel(file, {
|
||||
newVersion: this.versioning,
|
||||
parentId: this.rootFolderId,
|
||||
path: (file.webkitRelativePath || '').replace(/\/[^\/]*$/, '')
|
||||
}));
|
||||
this.uploadService.addToQueue(...latestFilesAdded);
|
||||
this.uploadService.uploadFilesInTheQueue(this.rootFolderId, path, this.onSuccess);
|
||||
this.uploadService.uploadFilesInTheQueue(this.onSuccess);
|
||||
if (this.showNotificationBar) {
|
||||
this.showUndoNotificationBar(latestFilesAdded);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* It converts the array given as input into a map. The map is a key values pairs, where the key is the directory name and the value are
|
||||
* all the files that the directory contains.
|
||||
* @param files - array of files
|
||||
* @returns {Map}
|
||||
*/
|
||||
private convertIntoHashMap(files: File[]): Map<string, File[]> {
|
||||
let directoryMap = new Map<string, File[]>();
|
||||
for (let file of files) {
|
||||
let directory = this.getDirectoryPath(file.webkitRelativePath);
|
||||
let filesSomeDir = directoryMap.get(directory) || [];
|
||||
filesSomeDir.push(file);
|
||||
directoryMap.set(directory, filesSomeDir);
|
||||
}
|
||||
return directoryMap;
|
||||
}
|
||||
|
||||
private getFiles(fileList: FileList): File[] {
|
||||
const result: File[] = [];
|
||||
|
||||
if (fileList && fileList.length > 0) {
|
||||
for (let i = 0; i < fileList.length; i++) {
|
||||
result.push(fileList[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Split the directory path given as input and cut the last directory name
|
||||
* @param directory
|
||||
* @returns {string}
|
||||
*/
|
||||
private getDirectoryPath(directory: string): string {
|
||||
let relativeDirPath = '';
|
||||
let dirPath = directory.split('/');
|
||||
if (dirPath.length > 1) {
|
||||
dirPath.pop();
|
||||
relativeDirPath = '/' + dirPath.join('/');
|
||||
}
|
||||
return relativeDirPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Split a directory path passed in input and return the first directory name
|
||||
* @param directory
|
||||
* @returns {string}
|
||||
*/
|
||||
private getDirectoryName(directory: string): string {
|
||||
let dirPath = directory.split('/');
|
||||
if (dirPath.length > 1) {
|
||||
return dirPath.pop();
|
||||
} else {
|
||||
return dirPath[0];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Show undo notification bar.
|
||||
*
|
||||
@@ -267,43 +188,6 @@ export class UploadButtonComponent implements OnInit, OnChanges {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrive the error message using the error status code
|
||||
* @param response - object that contain the HTTP response
|
||||
* @returns {string}
|
||||
*/
|
||||
private getErrorMessage(response: any): string {
|
||||
if (response && response.body && response.body.error.statusCode === ERROR_FOLDER_ALREADY_EXIST) {
|
||||
let errorMessage: any;
|
||||
errorMessage = this.translateService.get('FILE_UPLOAD.MESSAGES.FOLDER_ALREADY_EXIST');
|
||||
return errorMessage.value;
|
||||
}
|
||||
return 'Error';
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the error inside Notification bar
|
||||
* @param Error message
|
||||
* @private
|
||||
*/
|
||||
private showErrorNotificationBar(errorMessage: string): void {
|
||||
this.notificationService.openSnackMessage(errorMessage, 3000);
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace a placeholder {0} in a message with the input keys
|
||||
* @param message - the message that conains the placeholder
|
||||
* @param keys - array of value
|
||||
* @returns {string} - The message without placeholder
|
||||
*/
|
||||
private formatString(message: string, keys: any []): string {
|
||||
let i = keys.length;
|
||||
while (i--) {
|
||||
message = message.replace(new RegExp('\\{' + i + '\\}', 'gm'), keys[i]);
|
||||
}
|
||||
return message;
|
||||
}
|
||||
|
||||
checkPermission() {
|
||||
if (this.rootFolderId) {
|
||||
this.getFolderNode(this.rootFolderId).subscribe(
|
||||
@@ -313,6 +197,7 @@ export class UploadButtonComponent implements OnInit, OnChanges {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: move to AlfrescoContentService
|
||||
getFolderNode(nodeId: string): Observable<MinimalNodeEntryEntity> {
|
||||
let opts: any = {
|
||||
includeSource: true,
|
||||
@@ -331,13 +216,9 @@ export class UploadButtonComponent implements OnInit, OnChanges {
|
||||
}
|
||||
|
||||
private hasCreatePermission(node: any): boolean {
|
||||
if (this.hasPermissions(node)) {
|
||||
if (node && node.allowableOperations) {
|
||||
return node.allowableOperations.find(permision => permision === 'create') ? true : false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private hasPermissions(node: any): boolean {
|
||||
return node && node.allowableOperations ? true : false;
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user