diff --git a/demo-shell-ng2/app/components/files/files.component.html b/demo-shell-ng2/app/components/files/files.component.html index 2802b5753b..09424ed0b8 100644 --- a/demo-shell-ng2/app/components/files/files.component.html +++ b/demo-shell-ng2/app/components/files/files.component.html @@ -46,11 +46,10 @@ [contextMenuActions]="true" [contentActions]="true" [allowDropFiles]="true" - [sorting]="['name', 'desc']" (error)="onNavigationError($event)" (success)="resetError()" (preview)="showFile($event)" - (permissionError)="onPermissionsFailed($event)"> + (permissionError)="handlePermissionError($event)"> - - - - @@ -124,29 +113,14 @@ title="{{'DOCUMENT_LIST.ACTIONS.DOCUMENT.DOWNLOAD' | translate}}" handler="download"> - - - - - - @@ -195,7 +169,7 @@ [uploadFolders]="folderUpload" [versioning]="versioning" [disableWithNoPermission]="disableWithNoPermission" - (permissionEvent)="onUploadPermissionFailed($event)"> + (permissionEvent)="handlePermissionError($event)">
@@ -209,7 +183,7 @@ [uploadFolders]="folderUpload" [versioning]="versioning" [disableWithNoPermission]="disableWithNoPermission" - (permissionEvent)="onUploadPermissionFailed($event)"> + (permissionEvent)="handlePermissionError($event)">
diff --git a/demo-shell-ng2/app/components/files/files.component.ts b/demo-shell-ng2/app/components/files/files.component.ts index 92535891b6..f86b23a188 100644 --- a/demo-shell-ng2/app/components/files/files.component.ts +++ b/demo-shell-ng2/app/components/files/files.component.ts @@ -15,13 +15,12 @@ * limitations under the License. */ -import { Component, Input, OnInit, AfterViewInit, Optional, ViewChild, ChangeDetectorRef } from '@angular/core'; -import { ActivatedRoute, Params, Router } from '@angular/router'; +import { Component, Input, OnInit, Optional, ViewChild, ChangeDetectorRef } from '@angular/core'; +import { ActivatedRoute, Params } from '@angular/router'; import { MdDialog } from '@angular/material'; -import { AlfrescoAuthenticationService, AlfrescoContentService, FolderCreatedEvent, LogService, NotificationService } from 'ng2-alfresco-core'; -import { DocumentActionsService, DocumentListComponent, ContentActionHandler, DocumentActionModel, FolderActionModel } from 'ng2-alfresco-documentlist'; -import { FormService } from 'ng2-activiti-form'; -import { UploadService, UploadButtonComponent, UploadDragAreaComponent } from 'ng2-alfresco-upload'; +import { AlfrescoContentService, FolderCreatedEvent, NotificationService } from 'ng2-alfresco-core'; +import { DocumentListComponent } from 'ng2-alfresco-documentlist'; +import { UploadService, FileUploadCompleteEvent } from 'ng2-alfresco-upload'; import { CreateFolderDialog } from '../../dialogs/create-folder.dialog'; @@ -30,7 +29,7 @@ import { CreateFolderDialog } from '../../dialogs/create-folder.dialog'; templateUrl: './files.component.html', styleUrls: ['./files.component.css'] }) -export class FilesComponent implements OnInit, AfterViewInit { +export class FilesComponent implements OnInit { // The identifier of a node. You can also use one of these well-known aliases: -my- | -shared- | -root- currentFolderId: string = '-my-'; @@ -38,7 +37,7 @@ export class FilesComponent implements OnInit, AfterViewInit { fileNodeId: any; fileShowed: boolean = false; - useCustomToolbar = false; + useCustomToolbar = true; @Input() multipleFileUpload: boolean = false; @@ -64,36 +63,12 @@ export class FilesComponent implements OnInit, AfterViewInit { @ViewChild(DocumentListComponent) documentList: DocumentListComponent; - @ViewChild(UploadButtonComponent) - uploadButton: UploadButtonComponent; - - @ViewChild(UploadDragAreaComponent) - uploadDragArea: UploadDragAreaComponent; - - constructor(private documentActions: DocumentActionsService, - private authService: AlfrescoAuthenticationService, - private formService: FormService, - private logService: LogService, - private changeDetector: ChangeDetectorRef, - private router: Router, + constructor(private changeDetector: ChangeDetectorRef, private notificationService: NotificationService, private uploadService: UploadService, private contentService: AlfrescoContentService, private dialog: MdDialog, @Optional() private route: ActivatedRoute) { - documentActions.setHandler('my-handler', this.myDocumentActionHandler.bind(this)); - } - - myDocumentActionHandler(obj: any) { - window.alert('my custom action handler'); - } - - myCustomAction1(event) { - alert('Custom document action for ' + event.value.entry.name); - } - - myFolderAction1(event) { - alert('Custom folder action for ' + event.value.entry.name); } showFile(event) { @@ -120,36 +95,11 @@ export class FilesComponent implements OnInit, AfterViewInit { } }); } - if (this.authService.isBpmLoggedIn()) { - this.formService.getProcessDefinitions().subscribe( - defs => this.setupBpmActions(defs || []), - err => this.logService.error(err) - ); - } else { - this.logService.warn('You are not logged in to BPM'); - } + this.uploadService.fileUploadComplete.debounceTime(300).subscribe(value => this.onFileUploadComplete(value)); this.contentService.folderCreated.subscribe(value => this.onFolderCreated(value)); } - ngAfterViewInit() { - this.uploadButton.onSuccess - .debounceTime(100) - .subscribe((event) => { - this.reload(event); - }); - - this.uploadDragArea.onSuccess - .debounceTime(100) - .subscribe((event) => { - this.reload(event); - }); - } - - viewActivitiForm(event?: any) { - this.router.navigate(['/activiti/tasksnode', event.value.entry.id]); - } - onNavigationError(err: any) { if (err) { this.errorMessage = err.message || 'Navigation error'; @@ -160,24 +110,10 @@ export class FilesComponent implements OnInit, AfterViewInit { this.errorMessage = null; } - private setupBpmActions(actions: any[]) { - actions.map(def => { - let documentAction = new DocumentActionModel(); - documentAction.title = 'Activiti: ' + (def.name || 'Unknown process'); - documentAction.handler = this.getBpmActionHandler(def); - this.documentList.actions.push(documentAction); - - let folderAction = new FolderActionModel(); - folderAction.title = 'Activiti: ' + (def.name || 'Unknown process'); - folderAction.handler = this.getBpmActionHandler(def); - this.documentList.actions.push(folderAction); - }); - } - - private getBpmActionHandler(processDefinition: any): ContentActionHandler { - return function (obj: any, target?: any) { - window.alert(`Starting BPM process: ${processDefinition.id}`); - }.bind(this); + onFileUploadComplete(event: FileUploadCompleteEvent) { + if (event && event.file.options.parentId === this.documentList.currentFolderId) { + this.documentList.reload(); + } } onFolderCreated(event: FolderCreatedEvent) { @@ -188,20 +124,11 @@ export class FilesComponent implements OnInit, AfterViewInit { } } - onPermissionsFailed(event: any) { - this.notificationService.openSnackMessage(`you don't have the ${event.permission} permission to ${event.action} the ${event.type} `, 4000); - } - - onUploadPermissionFailed(event: any) { - this.notificationService.openSnackMessage(`you don't have the ${event.permission} permission to ${event.action} the ${event.type} `, 4000); - } - - reload(event: any) { - if (event && event.value && event.value.entry && event.value.entry.parentId) { - if (this.documentList.currentFolderId === event.value.entry.parentId) { - this.documentList.reload(); - } - } + handlePermissionError(event: any) { + this.notificationService.openSnackMessage( + `You don't have the ${event.permission} permission to ${event.action} the ${event.type} `, + 4000 + ); } onCreateFolderClicked(event: Event) { @@ -209,12 +136,8 @@ export class FilesComponent implements OnInit, AfterViewInit { dialogRef.afterClosed().subscribe(folderName => { if (folderName) { this.contentService.createFolder('', folderName, this.documentList.currentFolderId).subscribe( - node => { - console.log(node); - }, - err => { - console.log(err); - } + node => console.log(node), + err => console.log(err) ); } }); diff --git a/ng2-components/ng2-alfresco-core/src/directives/upload.directive.spec.ts b/ng2-components/ng2-alfresco-core/src/directives/upload.directive.spec.ts index 52b550fce0..8ba2658f0a 100644 --- a/ng2-components/ng2-alfresco-core/src/directives/upload.directive.spec.ts +++ b/ng2-components/ng2-alfresco-core/src/directives/upload.directive.spec.ts @@ -17,6 +17,7 @@ import { ElementRef } from '@angular/core'; import { UploadDirective } from './upload.directive'; +import { FileInfo } from './../utils/file-utils'; describe('UploadDirective', () => { @@ -106,30 +107,35 @@ describe('UploadDirective', () => { expect(event.preventDefault).not.toHaveBeenCalled(); }); - it('should raise upload-files event on files drop', () => { + it('should raise upload-files event on files drop', (done) => { directive.enabled = true; - let files = [ {}]; let event = jasmine.createSpyObj('event', ['preventDefault', 'stopPropagation']); spyOn(directive, 'getDataTransfer').and.returnValue({}); - spyOn(directive, 'getFilesDropped').and.returnValue(files); - spyOn(nativeElement, 'dispatchEvent').and.stub(); + spyOn(directive, 'getFilesDropped').and.returnValue(Promise.resolve([ + {}, + {} + ])); + spyOn(nativeElement, 'dispatchEvent').and.callFake(_ => { + done(); + }); directive.onDrop(event); - expect(nativeElement.dispatchEvent).toHaveBeenCalled(); }); - it('should provide dropped files in upload-files event', () => { + it('should provide dropped files in upload-files event', (done) => { directive.enabled = true; - let files = [ {}]; + let files = [ + {} + ]; let event = jasmine.createSpyObj('event', ['preventDefault', 'stopPropagation']); spyOn(directive, 'getDataTransfer').and.returnValue({}); - spyOn(directive, 'getFilesDropped').and.returnValue(files); + spyOn(directive, 'getFilesDropped').and.returnValue(Promise.resolve(files)); spyOn(nativeElement, 'dispatchEvent').and.callFake(e => { expect(e.detail.files.length).toBe(1); expect(e.detail.files[0]).toBe(files[0]); + done(); }); directive.onDrop(event); - expect(nativeElement.dispatchEvent).toHaveBeenCalled(); }); }); diff --git a/ng2-components/ng2-alfresco-core/src/directives/upload.directive.ts b/ng2-components/ng2-alfresco-core/src/directives/upload.directive.ts index 6a5e4f0aa4..4c6727f852 100644 --- a/ng2-components/ng2-alfresco-core/src/directives/upload.directive.ts +++ b/ng2-components/ng2-alfresco-core/src/directives/upload.directive.ts @@ -16,6 +16,7 @@ */ import { Directive, Input, HostListener, ElementRef, Renderer, OnInit, NgZone, OnDestroy } from '@angular/core'; +import { FileUtils, FileInfo } from '../utils/file-utils'; @Directive({ selector: '[adf-upload]' @@ -129,14 +130,16 @@ export class UploadDirective implements OnInit, OnDestroy { const dataTranfer = this.getDataTransfer(event); if (dataTranfer) { - const files = this.getFilesDropped(dataTranfer); - this.onUploadFiles(files); + this.getFilesDropped(dataTranfer).then(files => { + this.onUploadFiles(files); + }); + } } return false; } - onUploadFiles(files: File[]) { + onUploadFiles(files: FileInfo[]) { if (this.enabled && files.length > 0) { let e = new CustomEvent('upload-files', { detail: { @@ -177,34 +180,55 @@ export class UploadDirective implements OnInit, OnDestroy { * Extract files from the DataTransfer object used to hold the data that is being dragged during a drag and drop operation. * @param dataTransfer DataTransfer object */ - protected getFilesDropped(dataTransfer: DataTransfer): File[] { - const result: File[] = []; + protected getFilesDropped(dataTransfer: DataTransfer): Promise { + return new Promise(resolve => { + const iterations = []; - if (dataTransfer) { - const items: FileList = dataTransfer.files; + if (dataTransfer) { + const items = dataTransfer.items; + if (items) { + for (let i = 0; i < items.length; i++) { + if (typeof items[i].webkitGetAsEntry !== 'undefined') { + let item = items[i].webkitGetAsEntry(); + if (item) { + if (item.isFile) { + iterations.push(Promise.resolve( { + entry: item, + file: items[i].getAsFile(), + relativeFolder: '/' + })); + } else if (item.isDirectory) { + iterations.push(new Promise(resolveFolder => { + FileUtils.flattern(item).then(files => resolveFolder(files)); + })); + } + } + } else { + iterations.push(Promise.resolve({ + entry: null, + file: items[i].getAsFile(), + relativeFolder: '/' + })); + } + } + } else { + // safari or FF + let files = FileUtils + .toFileArray(dataTransfer.files) + .map(file => { + entry: null, + file: file, + relativeFolder: '/' + }); - if (items && items.length > 0) { - for (let i = 0; i < items.length; i++) { - result.push(items[i]); + iterations.push(Promise.resolve(files)); } } - } - return result; - } - - /** - * Extract files from the FileList object used to hold files that user selected by means of File Dialog. - * @param fileList List of selected files - */ - protected getFilesSelected(fileList: FileList) { - let result: File[] = []; - if (fileList && fileList.length > 0) { - for (let i = 0; i < fileList.length; i++) { - result.push(fileList[i]); - } - } - return result; + Promise.all(iterations).then(result => { + resolve(result.reduce((a, b) => a.concat(b), [])); + }); + }); } /** @@ -214,8 +238,12 @@ export class UploadDirective implements OnInit, OnDestroy { protected onSelectFiles(e: Event) { if (this.isClickMode()) { const input = (e.currentTarget); - const files = this.getFilesSelected(input.files); - this.onUploadFiles(files); + const files = FileUtils.toFileArray(input.files); + this.onUploadFiles(files.map(file => { + entry: null, + file: file, + relativeFolder: '/' + })); } } } diff --git a/ng2-components/ng2-alfresco-core/src/utils/file-utils.ts b/ng2-components/ng2-alfresco-core/src/utils/file-utils.ts new file mode 100644 index 0000000000..a996f6f676 --- /dev/null +++ b/ng2-components/ng2-alfresco-core/src/utils/file-utils.ts @@ -0,0 +1,73 @@ +/*! + * @license + * Copyright 2016 Alfresco Software, Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export interface FileInfo { + entry?: WebKitFileEntry; + file?: File; + relativeFolder?: string; +} + +export class FileUtils { + + static flattern(folder: any): Promise { + let reader = folder.createReader(); + let files: FileInfo[] = []; + return new Promise(resolve => { + let iterations = []; + (function traverse() { + reader.readEntries((entries) => { + if (!entries.length) { + Promise.all(iterations).then(result => resolve(files)); + } else { + iterations.push(Promise.all(entries.map(entry => { + if (entry.isFile) { + return new Promise(resolveFile => { + entry.file(function (f: File) { + files.push({ + entry: entry, + file: f, + relativeFolder: entry.fullPath.replace(/\/[^\/]*$/, '') + }); + resolveFile(); + }); + }); + } else { + return FileUtils.flattern(entry).then(result => { + files.push(...result); + }); + } + }))); + // Try calling traverse() again for the same dir, according to spec + traverse(); + } + }); + })(); + }); + } + + static toFileArray(fileList: FileList): File[] { + let result = []; + + if (fileList && fileList.length > 0) { + for (let i = 0; i < fileList.length; i++) { + result.push(fileList[i]); + } + } + + return result; + } +} diff --git a/ng2-components/ng2-alfresco-core/src/utils/index.ts b/ng2-components/ng2-alfresco-core/src/utils/index.ts index f3ae4ef189..0040de2936 100644 --- a/ng2-components/ng2-alfresco-core/src/utils/index.ts +++ b/ng2-components/ng2-alfresco-core/src/utils/index.ts @@ -16,3 +16,4 @@ */ export * from './object-utils'; +export * from './file-utils'; diff --git a/ng2-components/ng2-alfresco-upload/README.md b/ng2-components/ng2-alfresco-upload/README.md index 93e18adbf9..266d4d3d9d 100644 --- a/ng2-components/ng2-alfresco-upload/README.md +++ b/ng2-components/ng2-alfresco-upload/README.md @@ -102,70 +102,16 @@ Follow the 3 steps below: ```html ``` -Example of an App that declares upload button component : - -```ts -import { NgModule, Component } from '@angular/core'; -import { BrowserModule } from '@angular/platform-browser'; -import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; -import { CoreModule, AlfrescoSettingsService, AlfrescoAuthenticationService } from 'ng2-alfresco-core'; -import { UploadModule } from 'ng2-alfresco-upload'; - -@Component({ - selector: 'alfresco-app-demo', - template: ` - - - - ` -}) -export class MyDemoApp { - - constructor(private authService: AlfrescoAuthenticationService, - private settingsService: AlfrescoSettingsService) { - settingsService.ecmHost = 'http://localhost:8080'; - - this.authService.login('admin', 'admin').subscribe( - ticket => console.log(ticket), - error => console.log(error) - ); - } - - public onSuccess(event: Object): void { - console.log('File uploaded'); - } -} - -@NgModule({ - imports: [ - BrowserModule, - CoreModule.forRoot(), - UploadModule.forRoot() - ], - declarations: [ MyDemoApp ], - bootstrap: [ MyDemoApp ] -}) -export class AppModule { } - -platformBrowserDynamic().bootstrapModule(AppModule); -``` - ### Events | Name | Description | @@ -177,11 +123,12 @@ platformBrowserDynamic().bootstrapModule(AppModule); | Name | Type | Default | Description | | --- | --- | --- | --- | | `disabled` | *boolean* | false | Toggle component disabled state | -| `showNotificationBar` | *boolean* | true | Hide/show notification bar | +| **(deprecated)** `showNotificationBar` | *boolean* | true | Hide/show notification bar. **Deprecated in 1.6.0: use UploadService events and NotificationService api instead.** | | `uploadFolders` | *boolean* | false | Allow/disallow upload folders (only for chrome) | | `multipleFiles` | *boolean* | false | Allow/disallow multiple files | | `acceptedFilesType` | *string* | * | array of allowed file extensions , example: ".jpg,.gif,.png,.svg" | -| `currentFolderPath` | *string* | '/Sites/swsdp/documentLibrary' | define the path where the files are uploaded | +| **(deprecated)** `currentFolderPath` | *string* | '/Sites/swsdp/documentLibrary' | define the path where the files are uploaded. **Deprecated in 1.6.0: use rootFolderId instead.** | +| `rootFolderId` | *string* | '-root-' | The ID of the root folder node. | | `versioning` | *boolean* | false | Versioning false is the default uploader behaviour and it rename using an integer suffix if there is a name clash. Versioning true to indicate that a major version should be created | | `staticTitle` | *string* | 'FILE_UPLOAD.BUTTON.UPLOAD_FILE' or 'FILE_UPLOAD.BUTTON.UPLOAD_FOLDER' string in the JSON text file | define the text of the upload button | | `disableWithNoPermission` | *boolean* | false | If the value is true and the user doesn't have the permission to delete the node the button will be disabled | @@ -199,7 +146,9 @@ You can subscribe to this event from your component and use the NotificationServ [rootFolderId]="currentFolderId" (permissionEvent)="onUploadPermissionFailed($event)"> +``` +```ts export class MyComponent { onUploadPermissionFailed(event: any) { @@ -232,61 +181,22 @@ The UploadButtonComponent provides the property disableWithNoPermission that can This component, provide a drag and drop are to upload files to alfresco. ```html - + +
+ DRAG HERE +
``` -Example of an App that declares upload drag and drop component: - ```ts -import { NgModule, Component } from '@angular/core'; -import { BrowserModule } from '@angular/platform-browser'; -import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; -import { CoreModule, AlfrescoSettingsService, AlfrescoAuthenticationService } from 'ng2-alfresco-core'; -import { UploadModule } from 'ng2-alfresco-upload'; - -@Component({ - selector: 'alfresco-app-demo', - template: ` - -
- DRAG HERE -
-
- - ` -}) -export class MyDemoApp { - - constructor(private authService: AlfrescoAuthenticationService, - private settingsService: AlfrescoSettingsService) { - settingsService.ecmHost = 'http://localhost:8080'; - - this.authService.login('admin', 'admin').subscribe( - ticket => console.log(ticket), - error => console.log(error) - ); - } +export class AppComponent { public onSuccess(event: Object): void { console.log('File uploaded'); } + } - -@NgModule({ - imports: [ - BrowserModule, - CoreModule.forRoot(), - UploadModule.forRoot() - ], - declarations: [ MyDemoApp ], - bootstrap: [ MyDemoApp ] -}) -export class AppModule { } - -platformBrowserDynamic().bootstrapModule(AppModule); ``` ### Events @@ -300,9 +210,9 @@ platformBrowserDynamic().bootstrapModule(AppModule); | Name | Type | Default | Description | | --- | --- | --- | --- | | `enabled` | *boolean* | true | Toggle component enabled state | -| `showNotificationBar` | *boolean* | true | Hide/show notification bar | -| `rootFolderId` | *string* | '-root-' | The ID of the root folder node. -| `currentFolderPath` | *string* | '/' | define the path where the files are uploaded | +| **(deprecated)** `showNotificationBar` | *boolean* | true | Hide/show notification bar. **Deprecated in 1.6.0: use UploadService events and NotificationService api instead.** | +| `rootFolderId` | *string* | '-root-' | The ID of the root folder node. | +| **(deprecated)** `currentFolderPath` | *string* | '/' | define the path where the files are uploaded. **Deprecated in 1.6.0: use rootFolderId instead.** | | `versioning` | *boolean* | false | Versioning false is the default uploader behaviour and it rename using an integer suffix if there is a name clash. Versioning true to indicate that a major version should be created | ## FileUploadingDialogComponent diff --git a/ng2-components/ng2-alfresco-upload/src/components/file-uploading-dialog.component.ts b/ng2-components/ng2-alfresco-upload/src/components/file-uploading-dialog.component.ts index bee9dc6f3e..582301c080 100644 --- a/ng2-components/ng2-alfresco-upload/src/components/file-uploading-dialog.component.ts +++ b/ng2-components/ng2-alfresco-upload/src/components/file-uploading-dialog.component.ts @@ -67,7 +67,6 @@ export class FileUploadingDialogComponent implements OnInit, OnDestroy { }); this.uploadService.fileUpload.subscribe(e => { - console.log(e); this.cd.detectChanges(); }); } diff --git a/ng2-components/ng2-alfresco-upload/src/components/upload-button.component.spec.ts b/ng2-components/ng2-alfresco-upload/src/components/upload-button.component.spec.ts index 3ade2ab971..b4774b303c 100644 --- a/ng2-components/ng2-alfresco-upload/src/components/upload-button.component.spec.ts +++ b/ng2-components/ng2-alfresco-upload/src/components/upload-button.component.spec.ts @@ -186,7 +186,7 @@ describe('UploadButtonComponent', () => { fixture.detectChanges(); component.onFilesAdded(fakeEvent); - expect(uploadService.uploadFilesInTheQueue).toHaveBeenCalledWith('-root-', '/root-fake-/sites-fake/folder-fake', null); + expect(uploadService.uploadFilesInTheQueue).toHaveBeenCalledWith(null); }); it('should call uploadFile with a custom root folder', () => { @@ -202,7 +202,7 @@ describe('UploadButtonComponent', () => { fixture.detectChanges(); component.onFilesAdded(fakeEvent); - expect(uploadService.uploadFilesInTheQueue).toHaveBeenCalledWith('-my-', '/root-fake-/sites-fake/folder-fake', null); + expect(uploadService.uploadFilesInTheQueue).toHaveBeenCalledWith(null); }); it('should create a folder and emit an File uploaded event', (done) => { @@ -228,21 +228,6 @@ describe('UploadButtonComponent', () => { component.onDirectoryAdded(fakeEvent); }); - it('should emit an onError event when the folder already exist', (done) => { - component.rootFolderId = '-my-'; - spyOn(contentService, 'createFolder').and.returnValue(Observable.throw(new Error(''))); - spyOn(component, 'getFolderNode').and.returnValue(Observable.of(fakeFolderNodeWithPermission)); - - component.ngOnChanges({ rootFolderId: new SimpleChange(null, component.rootFolderId, true) }); - - component.onError.subscribe(e => { - expect(e.value).toEqual('Error'); - done(); - }); - - component.onDirectoryAdded(fakeEvent); - }); - it('should by default the title of the button get from the JSON file', () => { let compiled = fixture.debugElement.nativeElement; fixture.detectChanges(); diff --git a/ng2-components/ng2-alfresco-upload/src/components/upload-button.component.ts b/ng2-components/ng2-alfresco-upload/src/components/upload-button.component.ts index 1545851d76..e62729a367 100644 --- a/ng2-components/ng2-alfresco-upload/src/components/upload-button.component.ts +++ b/ng2-components/ng2-alfresco-upload/src/components/upload-button.component.ts @@ -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 { - let directoryMap = new Map(); - 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 { 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; - } } diff --git a/ng2-components/ng2-alfresco-upload/src/components/upload-drag-area.component.spec.ts b/ng2-components/ng2-alfresco-upload/src/components/upload-drag-area.component.spec.ts index 5886254ed2..29db41972d 100644 --- a/ng2-components/ng2-alfresco-upload/src/components/upload-drag-area.component.spec.ts +++ b/ng2-components/ng2-alfresco-upload/src/components/upload-drag-area.component.spec.ts @@ -67,21 +67,22 @@ describe('UploadDragAreaComponent', () => { TestBed.resetTestingModule(); }); - it('should upload the list of files dropped', () => { + it('should upload the list of files dropped', (done) => { component.currentFolderPath = '/root-fake-/sites-fake/folder-fake'; component.onSuccess = null; component.showNotificationBar = false; - uploadService.addToQueue = jasmine.createSpy('addToQueue'); uploadService.uploadFilesInTheQueue = jasmine.createSpy('uploadFilesInTheQueue'); fixture.detectChanges(); const file = {name: 'fake-name-1', size: 10, webkitRelativePath: 'fake-folder1/fake-name-1.json'}; - let fileFake = new FileModel(file); - let filesList = [fileFake]; + let filesList = [file]; + + spyOn(uploadService, 'addToQueue').and.callFake((f: FileModel) => { + expect(f.file).toBe(file); + done(); + }); component.onFilesDropped(filesList); - expect(uploadService.addToQueue).toHaveBeenCalledWith(fileFake); - expect(uploadService.uploadFilesInTheQueue).toHaveBeenCalledWith('-root-', '/root-fake-/sites-fake/folder-fake', null); }); it('should show the loading messages in the notification bar when the files are dropped', () => { @@ -92,11 +93,11 @@ describe('UploadDragAreaComponent', () => { component.showUndoNotificationBar = jasmine.createSpy('_showUndoNotificationBar'); fixture.detectChanges(); - let fileFake = new FileModel( {name: 'fake-name-1', size: 10, webkitRelativePath: 'fake-folder1/fake-name-1.json'}); + let fileFake = {name: 'fake-name-1', size: 10, webkitRelativePath: 'fake-folder1/fake-name-1.json'}; let filesList = [fileFake]; component.onFilesDropped(filesList); - expect(uploadService.uploadFilesInTheQueue).toHaveBeenCalledWith('-root-', '/root-fake-/sites-fake/folder-fake', null); + expect(uploadService.uploadFilesInTheQueue).toHaveBeenCalledWith(null); expect(component.showUndoNotificationBar).toHaveBeenCalled(); }); @@ -119,8 +120,7 @@ describe('UploadDragAreaComponent', () => { }; component.onFilesEntityDropped(itemEntity); - expect(uploadService.uploadFilesInTheQueue) - .toHaveBeenCalledWith('-root-', '/root-fake-/sites-fake/document-library-fake/folder-fake/', null); + expect(uploadService.uploadFilesInTheQueue).toHaveBeenCalledWith(null); }); it('should upload a file with a custom root folder ID when dropped', () => { @@ -143,7 +143,6 @@ describe('UploadDragAreaComponent', () => { }; component.onFilesEntityDropped(itemEntity); - expect(uploadService.uploadFilesInTheQueue) - .toHaveBeenCalledWith('-my-', '/root-fake-/sites-fake/document-library-fake/folder-fake/', null); + expect(uploadService.uploadFilesInTheQueue).toHaveBeenCalledWith(null); }); }); diff --git a/ng2-components/ng2-alfresco-upload/src/components/upload-drag-area.component.ts b/ng2-components/ng2-alfresco-upload/src/components/upload-drag-area.component.ts index 799714a9b1..5ae8f614fb 100644 --- a/ng2-components/ng2-alfresco-upload/src/components/upload-drag-area.component.ts +++ b/ng2-components/ng2-alfresco-upload/src/components/upload-drag-area.component.ts @@ -16,12 +16,10 @@ */ import { Component, Input, Output, EventEmitter } from '@angular/core'; -import { AlfrescoTranslationService, AlfrescoContentService, LogService, NotificationService } from 'ng2-alfresco-core'; +import { AlfrescoTranslationService, NotificationService, FileUtils, FileInfo } from 'ng2-alfresco-core'; import { UploadService } from '../services/upload.service'; import { FileModel } from '../models/file.model'; -const ERROR_FOLDER_ALREADY_EXIST = 409; - @Component({ selector: 'alfresco-upload-drag-area', templateUrl: './upload-drag-area.component.html', @@ -29,31 +27,39 @@ const ERROR_FOLDER_ALREADY_EXIST = 409; }) export class UploadDragAreaComponent { - private static DEFAULT_ROOT_ID: string = '-root-'; - @Input() enabled: boolean = true; + /** + * @deprecated Deprecated in 1.6.0, you can use UploadService events and NotificationService api instead. + * + * @type {boolean} + * @memberof UploadButtonComponent + */ @Input() showNotificationBar: boolean = true; @Input() versioning: boolean = false; + /** + * @deprecated Deprecated in 1.6.0, this property is not used for couple of releases already. Use rootFolderId instead. + * + * @type {string} + * @memberof UploadDragAreaComponent + */ @Input() currentFolderPath: string = '/'; @Input() - rootFolderId: string = UploadDragAreaComponent.DEFAULT_ROOT_ID; + rootFolderId: string = '-root-'; @Output() onSuccess = new EventEmitter(); constructor(private uploadService: UploadService, private translateService: AlfrescoTranslationService, - private logService: LogService, - private notificationService: NotificationService, - private contentService: AlfrescoContentService) { + private notificationService: NotificationService) { if (translateService) { translateService.addTranslationFolder('ng2-alfresco-upload', 'assets/ng2-alfresco-upload'); } @@ -67,15 +73,18 @@ export class UploadDragAreaComponent { e.stopPropagation(); e.preventDefault(); if (this.enabled) { - let files: File[] = e.detail.files; + let files: FileInfo[] = e.detail.files; if (files && files.length > 0) { - const fileModels = files.map(f => new FileModel(f, { newVersion: this.versioning })); - if (e.detail.data.obj.entry.isFolder) { - let id = e.detail.data.obj.entry.id; - this.onFilesDropped(fileModels, id, '/'); - } else { - this.onFilesDropped(fileModels); + let parentId = this.rootFolderId; + if (e.detail.data && e.detail.data.obj.entry.isFolder) { + parentId = e.detail.data.obj.entry.id || this.rootFolderId; } + const fileModels = files.map(fileInfo => new FileModel(fileInfo.file, { + newVersion: this.versioning, + path: fileInfo.relativeFolder, + parentId: parentId + })); + this.uploadFiles(fileModels); } } } @@ -85,10 +94,15 @@ export class UploadDragAreaComponent { * * @param {File[]} files - files dropped in the drag area. */ - onFilesDropped(files: FileModel[], rootId?: string, directory?: string): void { + onFilesDropped(files: File[]): void { if (this.enabled && files.length) { - this.uploadService.addToQueue(...files); - this.uploadService.uploadFilesInTheQueue(rootId || this.rootFolderId, directory || this.currentFolderPath, this.onSuccess); + const fileModels = files.map(file => new FileModel(file, { + newVersion: this.versioning, + path: '/', + parentId: this.rootFolderId + })); + this.uploadService.addToQueue(...fileModels); + this.uploadService.uploadFilesInTheQueue(this.onSuccess); let latestFilesAdded = this.uploadService.getQueue(); if (this.showNotificationBar) { this.showUndoNotificationBar(latestFilesAdded); @@ -103,11 +117,13 @@ export class UploadDragAreaComponent { onFilesEntityDropped(item: any): void { if (this.enabled) { item.file((file: File) => { - const fileModel = new FileModel(file, { newVersion: this.versioning }); + const fileModel = new FileModel(file, { + newVersion: this.versioning, + parentId: this.rootFolderId, + path: item.fullPath.replace(item.name, '') + }); this.uploadService.addToQueue(fileModel); - let path = item.fullPath.replace(item.name, ''); - let filePath = this.currentFolderPath + path; - this.uploadService.uploadFilesInTheQueue(this.rootFolderId, filePath, this.onSuccess); + this.uploadService.uploadFilesInTheQueue(this.onSuccess); }); } } @@ -118,52 +134,22 @@ export class UploadDragAreaComponent { */ onFolderEntityDropped(folder: any): void { if (this.enabled && folder.isDirectory) { - let relativePath = folder.fullPath.replace(folder.name, ''); - relativePath = this.currentFolderPath + relativePath; - - this.contentService.createFolder(relativePath, folder.name, this.rootFolderId) - .subscribe( - message => { - this.onSuccess.emit({ - value: 'Created folder' - }); - let dirReader = folder.createReader(); - dirReader.readEntries((entries: any) => { - for (let i = 0; i < entries.length; i++) { - this._traverseFileTree(entries[i]); - } - if (this.showNotificationBar) { - let latestFilesAdded = this.uploadService.getQueue(); - this.showUndoNotificationBar(latestFilesAdded); - } - }); - }, - error => { - let errorMessagePlaceholder = this.getErrorMessage(error.response); - let errorMessage = this.formatString(errorMessagePlaceholder, [folder.name]); - if (this.showNotificationBar) { - this.showErrorNotificationBar(errorMessage); - } else { - this.logService.error(errorMessage); - } - - } - ); - } - } - - /** - * Travers all the files and folders, and create it on the alfresco. - * - * @param {Object} item - can contains files or folders. - */ - private _traverseFileTree(item: any): void { - if (item.isFile) { - this.onFilesEntityDropped(item); - } else { - if (item.isDirectory) { - this.onFolderEntityDropped(item); - } + FileUtils.flattern(folder).then(entries => { + let files = entries.map(entry => { + return new FileModel(entry.file, { + newVersion: this.versioning, + parentId: this.rootFolderId, + path: entry.relativeFolder + }); + }); + this.uploadService.addToQueue(...files); + /* @deprecated in 1.6.0 */ + if (this.showNotificationBar) { + let latestFilesAdded = this.uploadService.getQueue(); + this.showUndoNotificationBar(latestFilesAdded); + } + this.uploadService.uploadFilesInTheQueue(this.onSuccess); + }); } } @@ -182,41 +168,14 @@ export class UploadDragAreaComponent { }); } - /** - * Show the error inside Notification bar - * @param Error message - * @private - */ - showErrorNotificationBar(errorMessage: string) { - this.notificationService.openSnackMessage(errorMessage, 3000); - } - - /** - * 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.body.error.statusCode === ERROR_FOLDER_ALREADY_EXIST) { - let errorMessage: any; - errorMessage = this.translateService.get('FILE_UPLOAD.MESSAGES.FOLDER_ALREADY_EXIST'); - return errorMessage.value; - } - } - - /** - * 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 []) { - if (message) { - let i = keys.length; - while (i--) { - message = message.replace(new RegExp('\\{' + i + '\\}', 'gm'), keys[i]); + private uploadFiles(files: FileModel[]): void { + if (this.enabled && files.length) { + this.uploadService.addToQueue(...files); + this.uploadService.uploadFilesInTheQueue(this.onSuccess); + let latestFilesAdded = this.uploadService.getQueue(); + if (this.showNotificationBar) { + this.showUndoNotificationBar(latestFilesAdded); } } - return message; } } diff --git a/ng2-components/ng2-alfresco-upload/src/directives/file-draggable.directive.ts b/ng2-components/ng2-alfresco-upload/src/directives/file-draggable.directive.ts index 5932e2a5c3..0568883475 100644 --- a/ng2-components/ng2-alfresco-upload/src/directives/file-draggable.directive.ts +++ b/ng2-components/ng2-alfresco-upload/src/directives/file-draggable.directive.ts @@ -16,6 +16,7 @@ */ import { Directive, EventEmitter, Input, Output, OnInit, OnDestroy, ElementRef, NgZone } from '@angular/core'; +import { FileUtils } from 'ng2-alfresco-core'; @Directive({ selector: '[file-draggable]' @@ -28,7 +29,7 @@ export class FileDraggableDirective implements OnInit, OnDestroy { enabled: boolean = true; @Output() - onFilesDropped: EventEmitter = new EventEmitter(); + onFilesDropped: EventEmitter = new EventEmitter(); @Output() onFilesEntityDropped: EventEmitter = new EventEmitter(); @@ -73,16 +74,20 @@ export class FileDraggableDirective implements OnInit, OnDestroy { if (typeof items[i].webkitGetAsEntry !== 'undefined') { let item = items[i].webkitGetAsEntry(); if (item) { - this.traverseFileTree(item); + if (item.isFile) { + this.onFilesEntityDropped.emit(item); + } else if (item.isDirectory) { + this.onFolderEntityDropped.emit(item); + } } } else { - let files = event.dataTransfer.files; + let files = FileUtils.toFileArray(event.dataTransfer.files); this.onFilesDropped.emit(files); } } } else { // safari or FF - let files = event.dataTransfer.files; + let files = FileUtils.toFileArray(event.dataTransfer.files); this.onFilesDropped.emit(files); } @@ -90,22 +95,6 @@ export class FileDraggableDirective implements OnInit, OnDestroy { } } - /** - * Travers all the files and folders, and emit an event for each file or directory. - * - * @param {Object} item - can contains files or folders. - */ - private traverseFileTree(item: any): void { - if (item.isFile) { - let self = this; - self.onFilesEntityDropped.emit(item); - } else { - if (item.isDirectory) { - this.onFolderEntityDropped.emit(item); - } - } - } - /** * Change the style of the drag area when a file drag in. * diff --git a/ng2-components/ng2-alfresco-upload/src/models/file.model.ts b/ng2-components/ng2-alfresco-upload/src/models/file.model.ts index d9a0e12a8d..5edfe8fc3c 100644 --- a/ng2-components/ng2-alfresco-upload/src/models/file.model.ts +++ b/ng2-components/ng2-alfresco-upload/src/models/file.model.ts @@ -23,6 +23,8 @@ export interface FileUploadProgress { export interface FileUploadOptions { newVersion?: boolean; + parentId?: string; + path?: string; } export enum FileUploadStatus { diff --git a/ng2-components/ng2-alfresco-upload/src/services/upload.service.spec.ts b/ng2-components/ng2-alfresco-upload/src/services/upload.service.spec.ts index 6c4f09faa2..20dcd170dc 100644 --- a/ng2-components/ng2-alfresco-upload/src/services/upload.service.spec.ts +++ b/ng2-components/ng2-alfresco-upload/src/services/upload.service.spec.ts @@ -19,7 +19,7 @@ import { EventEmitter } from '@angular/core'; import { TestBed } from '@angular/core/testing'; import { CoreModule } from 'ng2-alfresco-core'; import { UploadService } from './upload.service'; -import { FileModel } from '../models/file.model'; +import { FileModel, FileUploadOptions } from '../models/file.model'; declare let jasmine: any; @@ -77,9 +77,12 @@ describe('UploadService', () => { expect(e.value).toBe('File uploaded'); done(); }); - let fileFake = new FileModel({name: 'fake-name', size: 10}); + let fileFake = new FileModel( + {name: 'fake-name', size: 10}, + { parentId: '-root-', path: 'fake-dir' } + ); service.addToQueue(fileFake); - service.uploadFilesInTheQueue('-root-', 'fake-dir', emitter); + service.uploadFilesInTheQueue(emitter); let request = jasmine.Ajax.requests.mostRecent(); expect(request.url).toBe('http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/nodes/-root-/children?autoRename=true'); @@ -99,9 +102,12 @@ describe('UploadService', () => { expect(e.value).toBe('Error file uploaded'); done(); }); - let fileFake = new FileModel({name: 'fake-name', size: 10}); + let fileFake = new FileModel( + {name: 'fake-name', size: 10}, + { parentId: '-root-' } + ); service.addToQueue(fileFake); - service.uploadFilesInTheQueue('-root-', '', emitter); + service.uploadFilesInTheQueue(emitter); expect(jasmine.Ajax.requests.mostRecent().url) .toBe('http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/nodes/-root-/children?autoRename=true'); @@ -121,7 +127,7 @@ describe('UploadService', () => { }); let fileFake = new FileModel({name: 'fake-name', size: 10}); service.addToQueue(fileFake); - service.uploadFilesInTheQueue('-root-', '', emitter); + service.uploadFilesInTheQueue(emitter); let file = service.getQueue(); service.cancelUpload(...file); @@ -132,7 +138,7 @@ describe('UploadService', () => { const filesFake = new FileModel({name: 'fake-name', size: 10}, { newVersion: true }); service.addToQueue(filesFake); - service.uploadFilesInTheQueue('-root-', '', emitter); + service.uploadFilesInTheQueue(emitter); expect(jasmine.Ajax.requests.mostRecent().url.endsWith('autoRename=true')).toBe(false); expect(jasmine.Ajax.requests.mostRecent().params.has('majorVersion')).toBe(true); @@ -145,9 +151,12 @@ describe('UploadService', () => { expect(e.value).toBe('File uploaded'); done(); }); - let filesFake = new FileModel({name: 'fake-name', size: 10}); + let filesFake = new FileModel( + {name: 'fake-name', size: 10}, + { parentId: '123', path: 'fake-dir' } + ); service.addToQueue(filesFake); - service.uploadFilesInTheQueue('123', 'fake-dir', emitter); + service.uploadFilesInTheQueue(emitter); let request = jasmine.Ajax.requests.mostRecent(); expect(request.url).toBe('http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/nodes/123/children?autoRename=true'); diff --git a/ng2-components/ng2-alfresco-upload/src/services/upload.service.ts b/ng2-components/ng2-alfresco-upload/src/services/upload.service.ts index 0787f476f9..c988227a70 100644 --- a/ng2-components/ng2-alfresco-upload/src/services/upload.service.ts +++ b/ng2-components/ng2-alfresco-upload/src/services/upload.service.ts @@ -17,7 +17,7 @@ import { EventEmitter, Injectable } from '@angular/core'; import { Subject } from 'rxjs/Rx'; -import { AlfrescoApiService, LogService } from 'ng2-alfresco-core'; +import { AlfrescoApiService } from 'ng2-alfresco-core'; import { FileUploadEvent, FileUploadCompleteEvent } from '../events/file.event'; import { FileModel, FileUploadProgress, FileUploadStatus } from '../models/file.model'; @@ -27,6 +27,7 @@ export class UploadService { private queue: FileModel[] = []; private cache: { [key: string]: any } = {}; private totalComplete: number = 0; + private activeTask: Promise = null; queueChanged: Subject = new Subject(); fileUpload: Subject = new Subject(); @@ -37,8 +38,18 @@ export class UploadService { fileUploadError: Subject = new Subject(); fileUploadComplete: Subject = new Subject(); - constructor(private apiService: AlfrescoApiService, - private logService: LogService) { + constructor(private apiService: AlfrescoApiService) { + } + + /** + * Checks whether the service is uploading a file. + * + * @returns {boolean} + * + * @memberof UploadService + */ + isUploading(): boolean { + return this.activeTask ? true : false; } /** @@ -67,52 +78,32 @@ export class UploadService { /** * Pick all the files in the queue that are not been uploaded yet and upload it into the directory folder. + * + * @param {EventEmitter} emitter @deprecated emitter to invoke on file status change + * + * @memberof UploadService */ - uploadFilesInTheQueue(rootId: string, directory: string, elementEmit: EventEmitter): void { - const files = this.getFilesToUpload(); + uploadFilesInTheQueue(emitter: EventEmitter): void { + if (!this.activeTask) { + let file = this.queue.find(f => f.status === FileUploadStatus.Pending); + if (file) { + this.onUploadStarting(file); - files.forEach((file: FileModel) => { - this.onUploadStarting(file); + const promise = this.beginUpload(file, emitter); + this.activeTask = promise; + this.cache[file.id] = promise; - const opts: any = { - renditions: 'doclib' - }; + let next = () => { + this.activeTask = null; + setTimeout(() => this.uploadFilesInTheQueue(emitter), 100); + }; - if (file.options.newVersion === true) { - opts.overwrite = true; - opts.majorVersion = true; - } else { - opts.autoRename = true; + promise.then( + () => next(), + () => next() + ); } - - const promise = this.apiService.getInstance().upload.uploadFile(file.file, directory, rootId, null, opts); - promise.on('progress', (progress: FileUploadProgress) => { - this.onUploadProgress(file, progress); - }) - .on('abort', () => { - this.onUploadAborted(file); - elementEmit.emit({ - value: 'File aborted' - }); - }) - .on('error', err => { - this.onUploadError(file, err); - elementEmit.emit({ - value: 'Error file uploaded' - }); - }) - .on('success', data => { - this.onUploadComplete(file); - elementEmit.emit({ - value: data - }); - }) - .catch((err) => { - this.onUploadError(file, err); - }); - - this.cache[file.id] = promise; - }); + } } cancelUpload(...files: FileModel[]) { @@ -131,6 +122,46 @@ export class UploadService { }); } + private beginUpload(file: FileModel, /* @deprecated */emitter: EventEmitter): any { + let opts: any = { + renditions: 'doclib' + }; + + if (file.options.newVersion === true) { + opts.overwrite = true; + opts.majorVersion = true; + } else { + opts.autoRename = true; + } + let promise = this.apiService.getInstance().upload.uploadFile( + file.file, + file.options.path, + file.options.parentId, + null, + opts + ); + promise.on('progress', (progress: FileUploadProgress) => { + this.onUploadProgress(file, progress); + }) + .on('abort', () => { + this.onUploadAborted(file); + emitter.emit({ value: 'File aborted' }); + }) + .on('error', err => { + this.onUploadError(file, err); + emitter.emit({ value: 'Error file uploaded' }); + }) + .on('success', data => { + this.onUploadComplete(file); + emitter.emit({ value: data }); + }) + .catch(err => { + this.onUploadError(file, err); + }); + + return promise; + } + private onUploadStarting(file: FileModel): void { if (file) { file.status = FileUploadStatus.Starting; @@ -200,11 +231,4 @@ export class UploadService { this.fileUploadAborted.next(event); } } - - private getFilesToUpload(): FileModel[] { - let filesToUpload = this.queue.filter(file => { - return file.status === FileUploadStatus.Pending; - }); - return filesToUpload; - } }