diff --git a/docs/content-services/upload-drag-area.component.md b/docs/content-services/upload-drag-area.component.md index dc4f7c012a..1beb68349a 100644 --- a/docs/content-services/upload-drag-area.component.md +++ b/docs/content-services/upload-drag-area.component.md @@ -34,6 +34,7 @@ export class AppComponent { | disabled | boolean | false | Toggle component disabled state | | parentId | string | '-root-' | The ID of the folder in which the files will be uploaded. | | versioning | boolean | false | Versioning false is the default uploader behaviour and it renames the file using an integer suffix if there is a name clash. Versioning true to indicate that a major version should be created | +| acceptedFilesType | `string` | `'*'` | List of allowed file extensions, for example: ".jpg,.gif,.png,.svg". | ### Events diff --git a/lib/content-services/upload/components/base-upload/upload-base.ts b/lib/content-services/upload/components/base-upload/upload-base.ts new file mode 100644 index 0000000000..89c3a73c6a --- /dev/null +++ b/lib/content-services/upload/components/base-upload/upload-base.ts @@ -0,0 +1,47 @@ +/*! + * @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. + */ + +import { FileModel } from '@alfresco/adf-core'; +import { Input } from '@angular/core'; + +export abstract class UploadBase { + + @Input() + acceptedFilesType: string = '*'; + + constructor() {} + /** + * Checks if the given file is allowed by the extension filters + * + * @param file FileModel + */ + protected isFileAcceptable(file: FileModel): boolean { + if (this.acceptedFilesType === '*') { + return true; + } + + const allowedExtensions = this.acceptedFilesType + .split(',') + .map(ext => ext.replace(/^\./, '')); + + if (allowedExtensions.indexOf(file.extension) !== -1) { + return true; + } + + return false; + } +} diff --git a/lib/content-services/upload/components/upload-button.component.ts b/lib/content-services/upload/components/upload-button.component.ts index 80103d7fa6..828a0581da 100644 --- a/lib/content-services/upload/components/upload-button.component.ts +++ b/lib/content-services/upload/components/upload-button.component.ts @@ -41,6 +41,7 @@ import { Observable } from 'rxjs/Observable'; import { Subject } from 'rxjs/Subject'; import { PermissionModel } from '../../document-list/models/permissions.model'; import 'rxjs/add/observable/throw'; +import { UploadBase } from './base-upload/upload-base'; @Component({ selector: 'adf-upload-button', @@ -51,7 +52,7 @@ import 'rxjs/add/observable/throw'; ], encapsulation: ViewEncapsulation.None }) -export class UploadButtonComponent implements OnInit, OnChanges, NodePermissionSubject { +export class UploadButtonComponent extends UploadBase implements OnInit, OnChanges, NodePermissionSubject { /** Toggles component disabled state (if there is no node permission checking). */ @Input() @@ -69,10 +70,6 @@ export class UploadButtonComponent implements OnInit, OnChanges, NodePermissionS @Input() versioning: boolean = false; - /** List of allowed file extensions, for example: ".jpg,.gif,.png,.svg". */ - @Input() - acceptedFilesType: string = '*'; - /** Sets a limit on the maximum size (in bytes) of a file to be uploaded. * Has no effect if undefined. */ @@ -118,6 +115,7 @@ export class UploadButtonComponent implements OnInit, OnChanges, NodePermissionS protected translateService: TranslationService, protected logService: LogService ) { + super(); } ngOnInit() { @@ -190,27 +188,6 @@ export class UploadButtonComponent implements OnInit, OnChanges, NodePermissionS }); } - /** - * Checks if the given file is allowed by the extension filters - * - * @param file FileModel - */ - protected isFileAcceptable(file: FileModel): boolean { - if (this.acceptedFilesType === '*') { - return true; - } - - const allowedExtensions = this.acceptedFilesType - .split(',') - .map(ext => ext.replace(/^\./, '')); - - if (allowedExtensions.indexOf(file.extension) !== -1) { - return true; - } - - return false; - } - /** * Checks if the given file is an acceptable size * diff --git a/lib/content-services/upload/components/upload-drag-area.component.spec.ts b/lib/content-services/upload/components/upload-drag-area.component.spec.ts index b36f1fada3..390a99affc 100644 --- a/lib/content-services/upload/components/upload-drag-area.component.spec.ts +++ b/lib/content-services/upload/components/upload-drag-area.component.spec.ts @@ -178,65 +178,111 @@ describe('UploadDragAreaComponent', () => { }); }); - it('should upload the list of files dropped', (done) => { - component.success = null; - uploadService.uploadFilesInTheQueue = jasmine.createSpy('uploadFilesInTheQueue'); + describe('Upload Files', () => { + let addToQueueSpy; - fixture.detectChanges(); - const file = { name: 'fake-name-1', size: 10, webkitRelativePath: 'fake-folder1/fake-name-1.json' }; - let filesList = [file]; + beforeEach(async(() => { + addToQueueSpy = spyOn(uploadService, 'addToQueue'); + })); - spyOn(uploadService, 'addToQueue').and.callFake((f: FileModel) => { - expect(f.file).toBe(file); - done(); - }); + it('should upload the list of files dropped', async(() => { + component.success = null; + uploadService.uploadFilesInTheQueue = jasmine.createSpy('uploadFilesInTheQueue'); + fixture.detectChanges(); + const file = { name: 'fake-name-1', size: 10, webkitRelativePath: 'fake-folder1/fake-name-1.json' }; + let filesList = [file]; + fixture.detectChanges(); + fixture.whenStable().then(() => { + addToQueueSpy.and.callFake((f: FileModel) => { + expect(f.file).toBe(file); + }); + component.onFilesDropped(filesList); + }); + })); - component.onFilesDropped(filesList); - }); + it('should only upload those files whose fileTypes are in acceptedFilesType', async(() => { + spyOn(uploadService, 'uploadFilesInTheQueue'); + component.success = null; + component.acceptedFilesType = '.jpg,.pdf'; + fixture.detectChanges(); + const files: File[] = [ + { name: 'phobos.jpg' }, + { name: 'deimos.pdf' }, + { name: 'ganymede.bmp' } + ]; + component.onFilesDropped(files); + fixture.whenStable().then(() => { + expect(uploadService.uploadFilesInTheQueue).toHaveBeenCalledWith(null); + const filesCalledWith = addToQueueSpy.calls.mostRecent().args; + expect(filesCalledWith.length).toBe(2, 'Files should contain two elements'); + expect(filesCalledWith[0].name).toBe('phobos.jpg'); + expect(filesCalledWith[1].name).toBe('deimos.pdf'); + }); + })); - it('should upload a file when dropped', () => { - component.success = null; + it('should upload a file if fileType is in acceptedFilesType', async(() => { + spyOn(uploadService, 'uploadFilesInTheQueue'); + component.success = null; + component.acceptedFilesType = '.png'; + fixture.detectChanges(); + let itemEntity = { + fullPath: '/folder-fake/file-fake.png', + isDirectory: false, + isFile: true, + name: 'file-fake.png', + file: (callbackFile) => { + let fileFake = new File(['fakefake'], 'file-fake.png', {type: 'image/png'}); + callbackFile(fileFake); + } + }; + fixture.whenStable().then(() => { + component.onFilesEntityDropped(itemEntity); + expect(uploadService.uploadFilesInTheQueue).toHaveBeenCalledWith(null); + }); + })); - fixture.detectChanges(); - spyOn(uploadService, 'uploadFilesInTheQueue'); + it('should not upload a file if fileType is not in acceptedFilesType', async(() => { + component.success = null; + component.acceptedFilesType = '.pdf'; + fixture.detectChanges(); + spyOn(uploadService, 'uploadFilesInTheQueue'); - let itemEntity = { - fullPath: '/folder-fake/file-fake.png', - isDirectory: false, - isFile: true, - name: 'file-fake.png', - file: (callbackFile) => { - let fileFake = new File(['fakefake'], 'file-fake.png', { type: 'image/png' }); - callbackFile(fileFake); - } - }; + let itemEntity = { + fullPath: '/folder-fake/file-fake.png', + isDirectory: false, + isFile: true, + name: 'file-fake.png', + file: (callbackFile) => { + let fileFake = new File(['fakefake'], 'file-fake.png', {type: 'image/png'}); + callbackFile(fileFake); + } + }; + fixture.whenStable().then(() => { + component.onFilesEntityDropped(itemEntity); + expect(uploadService.uploadFilesInTheQueue).not.toHaveBeenCalledWith(null); + }); + })); - component.onFilesEntityDropped(itemEntity); - expect(uploadService.uploadFilesInTheQueue).toHaveBeenCalledWith(null); - }); + it('should upload a file with a custom root folder ID when dropped', async(() => { + component.success = null; + fixture.detectChanges(); + spyOn(uploadService, 'uploadFilesInTheQueue'); - it('should upload a file with a custom root folder ID when dropped', () => { - component.success = null; + let itemEntity = { + fullPath: '/folder-fake/file-fake.png', + isDirectory: false, + isFile: true, + name: 'file-fake.png', + file: (callbackFile) => { + let fileFake = new File(['fakefake'], 'file-fake.png', {type: 'image/png'}); + callbackFile(fileFake); + } + }; + component.onFilesEntityDropped(itemEntity); + expect(uploadService.uploadFilesInTheQueue).toHaveBeenCalledWith(null); + })); - fixture.detectChanges(); - spyOn(uploadService, 'uploadFilesInTheQueue'); - - let itemEntity = { - fullPath: '/folder-fake/file-fake.png', - isDirectory: false, - isFile: true, - name: 'file-fake.png', - file: (callbackFile) => { - let fileFake = new File(['fakefake'], 'file-fake.png', { type: 'image/png' }); - callbackFile(fileFake); - } - }; - - component.onFilesEntityDropped(itemEntity); - expect(uploadService.uploadFilesInTheQueue).toHaveBeenCalledWith(null); - }); - - it('should upload a file when user has create permission on target folder', async(() => { + it('should upload a file when user has create permission on target folder', async(() => { let fakeItem = { fullPath: '/folder-fake/file-fake.png', isDirectory: false, @@ -248,22 +294,16 @@ describe('UploadDragAreaComponent', () => { } }; - fixture.detectChanges(); - spyOn(uploadService, 'uploadFilesInTheQueue').and.returnValue(Promise.resolve(fakeItem)); - component.success.subscribe((val) => { - expect(val).not.toBeNull(); - }); - let fakeCustomEvent: CustomEvent = new CustomEvent('CustomEvent', { - detail: { - data: getFakeShareDataRow(), - files: [fakeItem] - } - }); + detail: { + data: getFakeShareDataRow(), + files: [fakeItem] + } + }); component.onUploadFiles(fakeCustomEvent); })); - +}); describe('Events', () => { it('should raise an error if upload a file goes wrong', (done) => { let fakeItem = { diff --git a/lib/content-services/upload/components/upload-drag-area.component.ts b/lib/content-services/upload/components/upload-drag-area.component.ts index 89d3ac1214..fb70b54985 100644 --- a/lib/content-services/upload/components/upload-drag-area.component.ts +++ b/lib/content-services/upload/components/upload-drag-area.component.ts @@ -26,6 +26,7 @@ import { UploadService } from '@alfresco/adf-core'; import { Component, EventEmitter, forwardRef, Input, Output, ViewEncapsulation } from '@angular/core'; +import { UploadBase } from './base-upload/upload-base'; @Component({ selector: 'adf-upload-drag-area', @@ -36,7 +37,7 @@ import { Component, EventEmitter, forwardRef, Input, Output, ViewEncapsulation } ], encapsulation: ViewEncapsulation.None }) -export class UploadDragAreaComponent implements NodePermissionSubject { +export class UploadDragAreaComponent extends UploadBase implements NodePermissionSubject { /** Toggle component disabled state. */ @Input() @@ -63,6 +64,7 @@ export class UploadDragAreaComponent implements NodePermissionSubject { constructor(private uploadService: UploadService, private translateService: TranslationService, private notificationService: NotificationService) { + super(); } /** @@ -76,9 +78,8 @@ export class UploadDragAreaComponent implements NodePermissionSubject { newVersion: this.versioning, path: '/', parentId: this.parentId - })); - this.uploadService.addToQueue(...fileModels); - this.uploadService.uploadFilesInTheQueue(this.success); + })).filter(this.isFileAcceptable.bind(this)); + this.addNodeInUploadQueue(fileModels); } } @@ -95,8 +96,9 @@ export class UploadDragAreaComponent implements NodePermissionSubject { parentId: this.parentId, path: item.fullPath.replace(item.name, '') }); - - this.addNodeInUploadQueue([fileModel]); + if (this.isFileAcceptable(fileModel)) { + this.addNodeInUploadQueue([fileModel]); + } }); } } @@ -115,8 +117,7 @@ export class UploadDragAreaComponent implements NodePermissionSubject { parentId: this.parentId, path: entry.relativeFolder }); - }); - + }).filter(this.isFileAcceptable.bind(this)); this.addNodeInUploadQueue(files); }); } @@ -128,7 +129,7 @@ export class UploadDragAreaComponent implements NodePermissionSubject { this.uploadService.uploadFilesInTheQueue(this.success); this.uploadService.fileUploadError.subscribe((error) => { this.error.emit(error); - }); + }); } } @@ -172,7 +173,7 @@ export class UploadDragAreaComponent implements NodePermissionSubject { newVersion: this.versioning, path: fileInfo.relativeFolder, parentId: parentId - })); + })).filter(this.isFileAcceptable.bind(this)); this.addNodeInUploadQueue(fileModels); } } diff --git a/lib/content-services/upload/public-api.ts b/lib/content-services/upload/public-api.ts index 17375a05f1..a69b68236d 100644 --- a/lib/content-services/upload/public-api.ts +++ b/lib/content-services/upload/public-api.ts @@ -23,3 +23,5 @@ export * from './components/file-uploading-list.component'; export * from './components/file-uploading-list-row.component'; export * from './directives/file-draggable.directive'; + +export * from './components/base-upload/upload-base'; \ No newline at end of file