diff --git a/demo-shell-ng2/app/components/files/files.component.html b/demo-shell-ng2/app/components/files/files.component.html index 701a09374b..e5772ef0b2 100644 --- a/demo-shell-ng2/app/components/files/files.component.html +++ b/demo-shell-ng2/app/components/files/files.component.html @@ -1,4 +1,4 @@ - @@ -8,6 +8,10 @@ source="name" class="full-width name-column"> + + @@ -74,4 +78,4 @@ - + \ No newline at end of file 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 b375d65c0a..c0ceb65e54 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 @@ -76,6 +76,9 @@ export class UploadButtonComponent { @Input() acceptedFilesType: string = '*'; + @Input() + currentFolderPath: string = '/Sites/swsdp/documentLibrary'; + @Input() uploaddirectory: string = ''; @@ -92,15 +95,14 @@ export class UploadButtonComponent { @Optional() translate: TranslateService) { console.log('UploadComponent constructor', el); + let site = this.getSiteId(); + let container = this.getContainerId(); + this._uploaderService = new UploadService({ - url: 'http://192.168.99.100:8080/alfresco/service/api/upload', - withCredentials: true, - authToken: btoa('admin:admin'), - authTokenPrefix: 'Basic', fieldName: 'filedata', formFields: { - siteid: 'swsdp', - containerid: 'documentLibrary' + siteid: site, + containerid: container } }); @@ -171,4 +173,20 @@ export class UploadButtonComponent { private _showDialog(): void { this.fileUploadingDialogComponent.showDialog(); } + + /** + * Return the site from the path + * @returns {any} + */ + private getSiteId(): string { + return this.currentFolderPath.replace('/Sites/','').split('/')[0]; + } + + /** + * Return the container from the path + * @returns {any} + */ + private getContainerId(): string { + return this.currentFolderPath.replace('/Sites/','').split('/')[1]; + } } diff --git a/ng2-components/ng2-alfresco-upload/src/components/upload-drag-area.component.html b/ng2-components/ng2-alfresco-upload/src/components/upload-drag-area.component.html index e96794623e..59e660ae70 100644 --- a/ng2-components/ng2-alfresco-upload/src/components/upload-drag-area.component.html +++ b/ng2-components/ng2-alfresco-upload/src/components/upload-drag-area.component.html @@ -1,4 +1,7 @@ -
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 7d39da46c8..958bf038bb 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 @@ -55,21 +55,23 @@ export class UploadDragAreaComponent { @Input() uploaddirectory: string = ''; + @Input() + currentFolderPath: string = '/Sites/swsdp/documentLibrary'; + @Output() onSuccess = new EventEmitter(); constructor(public el: ElementRef) { console.log('UploadComponent constructor', el); + let site = this.getSiteId(); + let container = this.getContainerId(); + this._uploaderService = new UploadService({ - url: 'http://192.168.99.100:8080/alfresco/service/api/upload', - withCredentials: true, - authToken: btoa('admin:admin'), - authTokenPrefix: 'Basic', fieldName: 'filedata', formFields: { - siteid: 'swsdp', - containerid: 'documentLibrary' + siteid: site, + containerid: container } }); } @@ -90,10 +92,88 @@ export class UploadDragAreaComponent { } } + /** + * Called when the file are dropped in the drag area + * @param item - FileEntity + */ + onFilesEntityDropped(item: any): void { + let self = this; + item.file(function (file: any) { + self._uploaderService.addToQueue([file]); + let path = item.fullPath.replace(item.name, ''); + let filePath = self.uploaddirectory + path; + self._uploaderService.uploadFilesInTheQueue(filePath, self.onSuccess); + self.filesUploadingList = self._uploaderService.getQueue(); + if (self.showUploadDialog) { + self._showDialog(); + } + }); + } + + /** + * Called when a folder are dropped in the drag area + * @param folder - name of the dropped folder + */ + onFolderEntityDropped(folder: any): void { + if (folder.isDirectory) { + let relativePath = folder.fullPath.replace(folder.name, ''); + relativePath = this.currentFolderPath + relativePath; + + this._uploaderService.createFolder(relativePath, folder.name) + .subscribe( + message => { + let self = this; + let dirReader = folder.createReader(); + dirReader.readEntries(function (entries: any) { + for (let i = 0; i < entries.length; i++) { + self._traverseFileTree(entries[i]); + } + }); + }, + error => { + error; + } + ); + } + } + + /** + * 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) { + let self = this; + self.onFilesEntityDropped(item); + } else { + if (item.isDirectory) { + let self = this; + self.onFolderEntityDropped(item); + } + } + } + /** * Show the upload dialog. */ private _showDialog(): void { this.fileUploadingDialogComponent.showDialog(); } + + /** + * Return the site from the path + * @returns {string} + */ + private getSiteId(): string { + return this.currentFolderPath.replace('/Sites/','').split('/')[0]; + } + + /** + * Return the container from the path + * @returns {string} + */ + private getContainerId(): string { + return this.currentFolderPath.replace('/Sites/','').split('/')[1]; + } } 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 b315b7c450..33965cdd59 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 @@ -43,6 +43,12 @@ export class FileDraggableDirective { @Output() onFilesDropped: EventEmitter = new EventEmitter(); + @Output() + onFilesEntityDropped: EventEmitter = new EventEmitter(); + + @Output() + onFolderEntityDropped: EventEmitter = new EventEmitter(); + files: File []; private _inputFocusClass: boolean = false; @@ -75,25 +81,17 @@ export class FileDraggableDirective { } /** - * Travers all the files and folders, and emit an event for each file. + * 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; - item.file(function (file: File) { - self.onFilesDropped.emit([file]); - }); + self.onFilesEntityDropped.emit(item); } else { if (item.isDirectory) { - let self = this; - let dirReader = item.createReader(); - dirReader.readEntries(function (entries: any) { - for (let i = 0; i < entries.length; i++) { - self._traverseFileTree(entries[i]); - } - }); + this.onFolderEntityDropped.emit(item); } } } 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 83eb896e5d..e2d8c773e7 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 @@ -21,19 +21,13 @@ import { FileModel } from './../models/file.model'; declare let jasmine: any; -describe('AlfrescoUploadService', () => { - let service: UploadService, - options: any, - xhr: XMLHttpRequest, - doneFn: any, - errorFn: any; +let doneFn = jasmine.createSpy('success'); +let errorFn = jasmine.createSpy('error'); - beforeEach(() => { - jasmine.Ajax.install(); +class MockUploadService extends UploadService { - doneFn = jasmine.createSpy('success'); - errorFn = jasmine.createSpy('error'); - xhr = new XMLHttpRequest(); + createXMLHttpRequestInstance() { + let xhr = new XMLHttpRequest(); xhr.onreadystatechange = function () { if (this.readyState === this.DONE && this.status === 200) { doneFn(this.responseText); @@ -42,6 +36,16 @@ describe('AlfrescoUploadService', () => { } }; xhr.abort = jasmine.createSpy('abort'); + return xhr; + } +} + +describe('AlfrescoUploadService', () => { + let service: MockUploadService, + options: any; + + beforeEach(() => { + jasmine.Ajax.install(); options = { url: '/some/cool/url', @@ -54,7 +58,7 @@ describe('AlfrescoUploadService', () => { containerid: 'fakeFolder' } }; - service = new UploadService(options); + service = new MockUploadService(options); }); afterEach(() => { @@ -78,12 +82,27 @@ describe('AlfrescoUploadService', () => { }); it('should make XHR done request after the file is added in the queue', () => { - service.setXMLHttpRequest(xhr); + let mockUploadSuccessResponses = { + upload: { + success: { + status: 200, + responseText: '{"nodeRef":"workspace://SpacesStore/fake","fileName": "fake-name.png","status":' + + '{"code": 200,"name": "OK","description": "fake file uploaded successfully"}}' + } + } + }; let filesFake = [{name: 'fake-name', size: 10}]; service.addToQueue(filesFake); service.uploadFilesInTheQueue('', null); - expect(jasmine.Ajax.requests.mostRecent().url).toBe('/some/cool/url'); + + let request = jasmine.Ajax.requests.mostRecent(); + // request.respondWith(mockUploadSuccessResponses.upload.success); + expect(request.url).toBe('/some/cool/url'); + expect(request.method).toBe('POST'); + // expect(request.data()).toEqual({fileName: 'fake-name.png'}); + expect(doneFn).not.toHaveBeenCalled(); + console.log(mockUploadSuccessResponses); jasmine.Ajax.requests.mostRecent().respondWith({ 'status': 200, contentType: 'text/plain', @@ -93,12 +112,10 @@ describe('AlfrescoUploadService', () => { }); it('should make XHR error request after an error occur', () => { - service.setXMLHttpRequest(xhr); let filesFake = [{name: 'fake-name', size: 10}]; service.addToQueue(filesFake); service.uploadFilesInTheQueue('', null); expect(jasmine.Ajax.requests.mostRecent().url).toBe('/some/cool/url'); - expect(doneFn).not.toHaveBeenCalled(); jasmine.Ajax.requests.mostRecent().respondWith({ 'status': 404, contentType: 'text/plain', @@ -108,30 +125,27 @@ describe('AlfrescoUploadService', () => { }); it('should make XHR abort request after the xhr abort is called', () => { - service.setXMLHttpRequest(xhr); let filesFake = [{name: 'fake-name', size: 10}]; service.addToQueue(filesFake); service.uploadFilesInTheQueue('', null); let file = service.getQueue(); file[0].setAbort(); - expect(xhr.abort).toHaveBeenCalled(); + expect(file[0]._xmlHttpRequest.abort).toHaveBeenCalled(); }); it('should make XHR done request after the file is upload', () => { - service.setXMLHttpRequest(xhr); let filesFake = {name: 'fake-name', size: 10}; let uploadingFileModel = new FileModel(filesFake); service.uploadFile(uploadingFileModel, '', null); expect(jasmine.Ajax.requests.mostRecent().url).toBe('/some/cool/url'); - expect(doneFn).not.toHaveBeenCalled(); jasmine.Ajax.requests.mostRecent().respondWith({ 'status': 200, contentType: 'text/plain', - responseText: 'File uploaded' + responseText: 'Single File uploaded' }); - expect(doneFn).toHaveBeenCalledWith('File uploaded'); + expect(doneFn).toHaveBeenCalledWith('Single File uploaded'); }); }); 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 a6681b82f4..87484cb6d4 100644 --- a/ng2-components/ng2-alfresco-upload/src/services/upload.service.ts +++ b/ng2-components/ng2-alfresco-upload/src/services/upload.service.ts @@ -18,7 +18,8 @@ import { FileModel } from '../models/file.model'; import { EventEmitter } from 'angular2/core'; - +import { Observable } from 'rxjs/Observable'; +import { Response } from 'angular2/http'; /** * @@ -27,28 +28,77 @@ import { EventEmitter } from 'angular2/core'; * @returns {UploadService} . */ export class UploadService { - private _url: string; + private _host: string = 'http://192.168.99.100:8080'; + private _baseUrlPath: string = '/alfresco/api/-default-/public/alfresco/versions/1'; + private _url: string = '/alfresco/service/api/upload'; + private _method: string = 'POST'; - private _authTokenPrefix: string = 'Basic'; - private _authToken: string = undefined; private _fieldName: string = 'file'; private _formFields: Object = {}; - private _withCredentials: boolean; - private _xmlHttpRequest: XMLHttpRequest; private _queue: FileModel[] = []; + private _alfrescoClient: any; + constructor(private options: any) { console.log('UploadService constructor'); - this._withCredentials = options.withCredentials != null ? options.withCredentials : this._withCredentials; - this._url = options.url != null ? options.url : this._url; - this._authTokenPrefix = options.authTokenPrefix != null ? options.authTokenPrefix : this._authTokenPrefix; - this._authToken = options.authToken != null ? options.authToken : this._authToken; + this._alfrescoClient = this.getAlfrescoClient(); + + this._host = options.host || this._host; + this._baseUrlPath = options.baseUrlPath || this._baseUrlPath; this._fieldName = options.fieldName != null ? options.fieldName : this._fieldName; this._formFields = options.formFields != null ? options.formFields : this._formFields; } + /** + * Get the host + * @returns {string} + */ + public get host(): string { + return this._host; + } + + /** + * Set the host + * @param value + */ + public set host(value: string) { + this._host = value; + } + + /** + * Get the base url + * @returns {string} + */ + private getBaseUrl(): string { + return this._host + this._baseUrlPath; + } + + /** + * Get the token from the local storage + * @returns {any} + */ + private getAlfrescoTicket(): string { + return localStorage.getItem('token'); + } + + /** + * Get the alfresco client + * @returns {AlfrescoApi.ApiClient} + */ + private getAlfrescoClient() { + let defaultClient = new AlfrescoApi.ApiClient(); + defaultClient.basePath = this.getBaseUrl(); + + // Configure HTTP basic authorization: basicAuth + let basicAuth = defaultClient.authentications['basicAuth']; + basicAuth.username = 'ROLE_TICKET'; + basicAuth.password = this.getAlfrescoTicket(); + + return defaultClient; + } + /** * Add files to the uploading queue to be uploaded. * @@ -83,43 +133,43 @@ export class UploadService { }; /** - * The method create a new XMLHttpRequest instance if doesn't exist + * Create an XMLHttpRequest and return it + * @returns {XMLHttpRequest} */ - private _configureXMLHttpRequest(uploadingFileModel: any, elementEmit: EventEmitter) { - if (this._xmlHttpRequest === undefined) { - this._xmlHttpRequest = new XMLHttpRequest(); - this._xmlHttpRequest.upload.onprogress = (e) => { - if (e.lengthComputable) { - let percent = Math.round(e.loaded / e.total * 100); - uploadingFileModel.setProgres({ - total: e.total, - loaded: e.loaded, - percent: percent - }); - } - }; + createXMLHttpRequestInstance(uploadingFileModel: any, elementEmit: EventEmitter) { + let xmlHttpRequest = new XMLHttpRequest(); + xmlHttpRequest.upload.onprogress = (e) => { + if (e.lengthComputable) { + let percent = Math.round(e.loaded / e.total * 100); + uploadingFileModel.setProgres({ + total: e.total, + loaded: e.loaded, + percent: percent + }); + } + }; - this._xmlHttpRequest.upload.onabort = (e) => { - uploadingFileModel.setAbort(); - }; + xmlHttpRequest.upload.onabort = (e) => { + uploadingFileModel.setAbort(); + }; - this._xmlHttpRequest.upload.onerror = (e) => { - uploadingFileModel.setError(); - }; + xmlHttpRequest.upload.onerror = (e) => { + uploadingFileModel.setError(); + }; - this._xmlHttpRequest.onreadystatechange = () => { - if (this._xmlHttpRequest.readyState === XMLHttpRequest.DONE) { - elementEmit.emit({ - value: 'File uploaded' - }); - uploadingFileModel.onFinished( - this._xmlHttpRequest.status, - this._xmlHttpRequest.statusText, - this._xmlHttpRequest.response - ); - } - }; - } + xmlHttpRequest.onreadystatechange = () => { + if (xmlHttpRequest.readyState === XMLHttpRequest.DONE) { + elementEmit.emit({ + value: 'File uploaded' + }); + uploadingFileModel.onFinished( + xmlHttpRequest.status, + xmlHttpRequest.statusText, + xmlHttpRequest.response + ); + } + }; + return xmlHttpRequest; } /** @@ -129,6 +179,11 @@ export class UploadService { * */ uploadFile(uploadingFileModel: FileModel, directory: string, elementEmit: EventEmitter): void { + // Configure HTTP basic authorization: basicAuth + let basicAuth = this._alfrescoClient.authentications['basicAuth']; + basicAuth.username = 'ROLE_TICKET'; + basicAuth.password = this.getAlfrescoTicket(); + let form = new FormData(); form.append(this._fieldName, uploadingFileModel.file, uploadingFileModel.name); Object.keys(this._formFields).forEach((key: any) => { @@ -137,17 +192,15 @@ export class UploadService { form.append('uploaddirectory', directory); - this._configureXMLHttpRequest(uploadingFileModel, elementEmit); - uploadingFileModel.setXMLHttpRequest(this._xmlHttpRequest); + let xmlHttpRequest = this.createXMLHttpRequestInstance(uploadingFileModel, elementEmit); - this._xmlHttpRequest.open(this._method, this._url, true); - this._xmlHttpRequest.withCredentials = this._withCredentials; - - if (this._authToken) { - this._xmlHttpRequest.setRequestHeader('Authorization', `${this._authTokenPrefix} ${this._authToken}`); + xmlHttpRequest.open(this._method, this._host + this._url, true); + let authToken = btoa(basicAuth.username +':'+ basicAuth.password); + if (authToken) { + xmlHttpRequest.setRequestHeader('Authorization', `${basicAuth.type} ${authToken}`); } - this._xmlHttpRequest.send(form); + xmlHttpRequest.send(form); } /** @@ -169,10 +222,35 @@ export class UploadService { } /** - * Set XMLHttpRequest method - * @param xhr + * Create a folder + * @param name - the folder name */ - public setXMLHttpRequest(xhr: XMLHttpRequest) { - this._xmlHttpRequest = xhr; + createFolder(relativePath: string, name: string) { + console.log('Directory created' + name); + let apiInstance = new AlfrescoApi.NodesApi(this._alfrescoClient); + let nodeId = '-root-'; + let nodeBody = { + 'name':name, + 'nodeType':'cm:folder', + 'relativePath':relativePath + }; + return Observable.fromPromise(apiInstance.addNode(nodeId, nodeBody)) + .map(res => { + console.log(res); + } ) + .do(data => console.log('Node data', data)) // eyeball results in the console + .catch(this.handleError); + } + + /** + * Throw the error + * @param error + * @returns {ErrorObservable} + */ + private handleError(error: Response) { + // in a real world app, we may send the error to some remote logging infrastructure + // instead of just logging it to the console + console.error(error); + return Observable.throw(error || 'Server error'); } }