#60 Reproduce folders tree when a folder is uploaded

This commit is contained in:
mauriziovitale84 2016-05-19 10:59:31 +01:00
parent 2fa8283213
commit a9df957f2a
7 changed files with 298 additions and 103 deletions

View File

@ -1,4 +1,4 @@
<alfresco-upload-drag-area [showUploadDialog]="true" uploaddirectory="{{relativePath}}" <alfresco-upload-drag-area [showUploadDialog]="true" currentFolderPath="{{absolutePath}}" uploaddirectory="{{relativePath}}"
(onSuccess)="refreshDocumentList($event)"> (onSuccess)="refreshDocumentList($event)">
<alfresco-document-list (folderClick)="refreshDirectyory($event)" currentFolderPath="{{absolutePath}}"> <alfresco-document-list (folderClick)="refreshDirectyory($event)" currentFolderPath="{{absolutePath}}">
<content-columns> <content-columns>
@ -8,6 +8,10 @@
source="name" source="name"
class="full-width name-column"> class="full-width name-column">
</content-column> </content-column>
<content-column
title="{{'DOCUMENT_LIST.COLUMNS.SITE' | translate}}"
source="location.site">
</content-column>
<content-column <content-column
title="{{'DOCUMENT_LIST.COLUMNS.CREATED_BY' | translate}}" title="{{'DOCUMENT_LIST.COLUMNS.CREATED_BY' | translate}}"
source="createdByUser.displayName"> source="createdByUser.displayName">
@ -74,4 +78,4 @@
</alfresco-document-list> </alfresco-document-list>
</alfresco-upload-drag-area> </alfresco-upload-drag-area>
<alfresco-upload-button uploaddirectory="{{relativePath}}" (onSuccess)="refreshDocumentList($event)"></alfresco-upload-button> <alfresco-upload-button uploaddirectory="{{relativePath}}" currentFolderPath="{{absolutePath}}" (onSuccess)="refreshDocumentList($event)"></alfresco-upload-button>

View File

@ -76,6 +76,9 @@ export class UploadButtonComponent {
@Input() @Input()
acceptedFilesType: string = '*'; acceptedFilesType: string = '*';
@Input()
currentFolderPath: string = '/Sites/swsdp/documentLibrary';
@Input() @Input()
uploaddirectory: string = ''; uploaddirectory: string = '';
@ -92,15 +95,14 @@ export class UploadButtonComponent {
@Optional() translate: TranslateService) { @Optional() translate: TranslateService) {
console.log('UploadComponent constructor', el); console.log('UploadComponent constructor', el);
let site = this.getSiteId();
let container = this.getContainerId();
this._uploaderService = new UploadService({ 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', fieldName: 'filedata',
formFields: { formFields: {
siteid: 'swsdp', siteid: site,
containerid: 'documentLibrary' containerid: container
} }
}); });
@ -171,4 +173,20 @@ export class UploadButtonComponent {
private _showDialog(): void { private _showDialog(): void {
this.fileUploadingDialogComponent.showDialog(); 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];
}
} }

View File

@ -1,4 +1,7 @@
<div file-draggable id='UploadBorder' class="upload-border" (onFilesDropped)="onFilesDropped($event)" <div file-draggable id='UploadBorder' class="upload-border"
(onFilesDropped)="onFilesDropped($event)"
(onFilesEntityDropped)="onFilesEntityDropped($event)"
(onFolderEntityDropped)="onFolderEntityDropped($event)"
dropzone="" webkitdropzone="*" #droparea> dropzone="" webkitdropzone="*" #droparea>
<ng-content></ng-content> <ng-content></ng-content>
</div> </div>

View File

@ -55,21 +55,23 @@ export class UploadDragAreaComponent {
@Input() @Input()
uploaddirectory: string = ''; uploaddirectory: string = '';
@Input()
currentFolderPath: string = '/Sites/swsdp/documentLibrary';
@Output() @Output()
onSuccess = new EventEmitter(); onSuccess = new EventEmitter();
constructor(public el: ElementRef) { constructor(public el: ElementRef) {
console.log('UploadComponent constructor', el); console.log('UploadComponent constructor', el);
let site = this.getSiteId();
let container = this.getContainerId();
this._uploaderService = new UploadService({ 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', fieldName: 'filedata',
formFields: { formFields: {
siteid: 'swsdp', siteid: site,
containerid: 'documentLibrary' 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. * Show the upload dialog.
*/ */
private _showDialog(): void { private _showDialog(): void {
this.fileUploadingDialogComponent.showDialog(); 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];
}
} }

View File

@ -43,6 +43,12 @@ export class FileDraggableDirective {
@Output() @Output()
onFilesDropped: EventEmitter<any> = new EventEmitter(); onFilesDropped: EventEmitter<any> = new EventEmitter();
@Output()
onFilesEntityDropped: EventEmitter<any> = new EventEmitter();
@Output()
onFolderEntityDropped: EventEmitter<any> = new EventEmitter();
files: File []; files: File [];
private _inputFocusClass: boolean = false; 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. * @param {Object} item - can contains files or folders.
*/ */
private _traverseFileTree(item: any): void { private _traverseFileTree(item: any): void {
if (item.isFile) { if (item.isFile) {
let self = this; let self = this;
item.file(function (file: File) { self.onFilesEntityDropped.emit(item);
self.onFilesDropped.emit([file]);
});
} else { } else {
if (item.isDirectory) { if (item.isDirectory) {
let self = this; this.onFolderEntityDropped.emit(item);
let dirReader = item.createReader();
dirReader.readEntries(function (entries: any) {
for (let i = 0; i < entries.length; i++) {
self._traverseFileTree(entries[i]);
}
});
} }
} }
} }

View File

@ -21,19 +21,13 @@ import { FileModel } from './../models/file.model';
declare let jasmine: any; declare let jasmine: any;
describe('AlfrescoUploadService', () => { let doneFn = jasmine.createSpy('success');
let service: UploadService, let errorFn = jasmine.createSpy('error');
options: any,
xhr: XMLHttpRequest,
doneFn: any,
errorFn: any;
beforeEach(() => { class MockUploadService extends UploadService {
jasmine.Ajax.install();
doneFn = jasmine.createSpy('success'); createXMLHttpRequestInstance() {
errorFn = jasmine.createSpy('error'); let xhr = new XMLHttpRequest();
xhr = new XMLHttpRequest();
xhr.onreadystatechange = function () { xhr.onreadystatechange = function () {
if (this.readyState === this.DONE && this.status === 200) { if (this.readyState === this.DONE && this.status === 200) {
doneFn(this.responseText); doneFn(this.responseText);
@ -42,6 +36,16 @@ describe('AlfrescoUploadService', () => {
} }
}; };
xhr.abort = jasmine.createSpy('abort'); xhr.abort = jasmine.createSpy('abort');
return xhr;
}
}
describe('AlfrescoUploadService', () => {
let service: MockUploadService,
options: any;
beforeEach(() => {
jasmine.Ajax.install();
options = { options = {
url: '/some/cool/url', url: '/some/cool/url',
@ -54,7 +58,7 @@ describe('AlfrescoUploadService', () => {
containerid: 'fakeFolder' containerid: 'fakeFolder'
} }
}; };
service = new UploadService(options); service = new MockUploadService(options);
}); });
afterEach(() => { afterEach(() => {
@ -78,12 +82,27 @@ describe('AlfrescoUploadService', () => {
}); });
it('should make XHR done request after the file is added in the queue', () => { 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}]; let filesFake = [{name: 'fake-name', size: 10}];
service.addToQueue(filesFake); service.addToQueue(filesFake);
service.uploadFilesInTheQueue('', null); 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(); expect(doneFn).not.toHaveBeenCalled();
console.log(mockUploadSuccessResponses);
jasmine.Ajax.requests.mostRecent().respondWith({ jasmine.Ajax.requests.mostRecent().respondWith({
'status': 200, 'status': 200,
contentType: 'text/plain', contentType: 'text/plain',
@ -93,12 +112,10 @@ describe('AlfrescoUploadService', () => {
}); });
it('should make XHR error request after an error occur', () => { it('should make XHR error request after an error occur', () => {
service.setXMLHttpRequest(xhr);
let filesFake = [{name: 'fake-name', size: 10}]; let filesFake = [{name: 'fake-name', size: 10}];
service.addToQueue(filesFake); service.addToQueue(filesFake);
service.uploadFilesInTheQueue('', null); service.uploadFilesInTheQueue('', null);
expect(jasmine.Ajax.requests.mostRecent().url).toBe('/some/cool/url'); expect(jasmine.Ajax.requests.mostRecent().url).toBe('/some/cool/url');
expect(doneFn).not.toHaveBeenCalled();
jasmine.Ajax.requests.mostRecent().respondWith({ jasmine.Ajax.requests.mostRecent().respondWith({
'status': 404, 'status': 404,
contentType: 'text/plain', contentType: 'text/plain',
@ -108,30 +125,27 @@ describe('AlfrescoUploadService', () => {
}); });
it('should make XHR abort request after the xhr abort is called', () => { it('should make XHR abort request after the xhr abort is called', () => {
service.setXMLHttpRequest(xhr);
let filesFake = [{name: 'fake-name', size: 10}]; let filesFake = [{name: 'fake-name', size: 10}];
service.addToQueue(filesFake); service.addToQueue(filesFake);
service.uploadFilesInTheQueue('', null); service.uploadFilesInTheQueue('', null);
let file = service.getQueue(); let file = service.getQueue();
file[0].setAbort(); file[0].setAbort();
expect(xhr.abort).toHaveBeenCalled(); expect(file[0]._xmlHttpRequest.abort).toHaveBeenCalled();
}); });
it('should make XHR done request after the file is upload', () => { it('should make XHR done request after the file is upload', () => {
service.setXMLHttpRequest(xhr);
let filesFake = {name: 'fake-name', size: 10}; let filesFake = {name: 'fake-name', size: 10};
let uploadingFileModel = new FileModel(filesFake); let uploadingFileModel = new FileModel(filesFake);
service.uploadFile(uploadingFileModel, '', null); service.uploadFile(uploadingFileModel, '', null);
expect(jasmine.Ajax.requests.mostRecent().url).toBe('/some/cool/url'); expect(jasmine.Ajax.requests.mostRecent().url).toBe('/some/cool/url');
expect(doneFn).not.toHaveBeenCalled();
jasmine.Ajax.requests.mostRecent().respondWith({ jasmine.Ajax.requests.mostRecent().respondWith({
'status': 200, 'status': 200,
contentType: 'text/plain', contentType: 'text/plain',
responseText: 'File uploaded' responseText: 'Single File uploaded'
}); });
expect(doneFn).toHaveBeenCalledWith('File uploaded'); expect(doneFn).toHaveBeenCalledWith('Single File uploaded');
}); });
}); });

View File

@ -18,7 +18,8 @@
import { FileModel } from '../models/file.model'; import { FileModel } from '../models/file.model';
import { EventEmitter } from 'angular2/core'; 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} . * @returns {UploadService} .
*/ */
export class 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 _method: string = 'POST';
private _authTokenPrefix: string = 'Basic';
private _authToken: string = undefined;
private _fieldName: string = 'file'; private _fieldName: string = 'file';
private _formFields: Object = {}; private _formFields: Object = {};
private _withCredentials: boolean;
private _xmlHttpRequest: XMLHttpRequest;
private _queue: FileModel[] = []; private _queue: FileModel[] = [];
private _alfrescoClient: any;
constructor(private options: any) { constructor(private options: any) {
console.log('UploadService constructor'); console.log('UploadService constructor');
this._withCredentials = options.withCredentials != null ? options.withCredentials : this._withCredentials; this._alfrescoClient = this.getAlfrescoClient();
this._url = options.url != null ? options.url : this._url;
this._authTokenPrefix = options.authTokenPrefix != null ? options.authTokenPrefix : this._authTokenPrefix; this._host = options.host || this._host;
this._authToken = options.authToken != null ? options.authToken : this._authToken; this._baseUrlPath = options.baseUrlPath || this._baseUrlPath;
this._fieldName = options.fieldName != null ? options.fieldName : this._fieldName; this._fieldName = options.fieldName != null ? options.fieldName : this._fieldName;
this._formFields = options.formFields != null ? options.formFields : this._formFields; 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. * 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<any>) { createXMLHttpRequestInstance(uploadingFileModel: any, elementEmit: EventEmitter<any>) {
if (this._xmlHttpRequest === undefined) { let xmlHttpRequest = new XMLHttpRequest();
this._xmlHttpRequest = new XMLHttpRequest(); xmlHttpRequest.upload.onprogress = (e) => {
this._xmlHttpRequest.upload.onprogress = (e) => { if (e.lengthComputable) {
if (e.lengthComputable) { let percent = Math.round(e.loaded / e.total * 100);
let percent = Math.round(e.loaded / e.total * 100); uploadingFileModel.setProgres({
uploadingFileModel.setProgres({ total: e.total,
total: e.total, loaded: e.loaded,
loaded: e.loaded, percent: percent
percent: percent });
}); }
} };
};
this._xmlHttpRequest.upload.onabort = (e) => { xmlHttpRequest.upload.onabort = (e) => {
uploadingFileModel.setAbort(); uploadingFileModel.setAbort();
}; };
this._xmlHttpRequest.upload.onerror = (e) => { xmlHttpRequest.upload.onerror = (e) => {
uploadingFileModel.setError(); uploadingFileModel.setError();
}; };
this._xmlHttpRequest.onreadystatechange = () => { xmlHttpRequest.onreadystatechange = () => {
if (this._xmlHttpRequest.readyState === XMLHttpRequest.DONE) { if (xmlHttpRequest.readyState === XMLHttpRequest.DONE) {
elementEmit.emit({ elementEmit.emit({
value: 'File uploaded' value: 'File uploaded'
}); });
uploadingFileModel.onFinished( uploadingFileModel.onFinished(
this._xmlHttpRequest.status, xmlHttpRequest.status,
this._xmlHttpRequest.statusText, xmlHttpRequest.statusText,
this._xmlHttpRequest.response xmlHttpRequest.response
); );
} }
}; };
} return xmlHttpRequest;
} }
/** /**
@ -129,6 +179,11 @@ export class UploadService {
* *
*/ */
uploadFile(uploadingFileModel: FileModel, directory: string, elementEmit: EventEmitter<any>): void { uploadFile(uploadingFileModel: FileModel, directory: string, elementEmit: EventEmitter<any>): void {
// Configure HTTP basic authorization: basicAuth
let basicAuth = this._alfrescoClient.authentications['basicAuth'];
basicAuth.username = 'ROLE_TICKET';
basicAuth.password = this.getAlfrescoTicket();
let form = new FormData(); let form = new FormData();
form.append(this._fieldName, uploadingFileModel.file, uploadingFileModel.name); form.append(this._fieldName, uploadingFileModel.file, uploadingFileModel.name);
Object.keys(this._formFields).forEach((key: any) => { Object.keys(this._formFields).forEach((key: any) => {
@ -137,17 +192,15 @@ export class UploadService {
form.append('uploaddirectory', directory); form.append('uploaddirectory', directory);
this._configureXMLHttpRequest(uploadingFileModel, elementEmit); let xmlHttpRequest = this.createXMLHttpRequestInstance(uploadingFileModel, elementEmit);
uploadingFileModel.setXMLHttpRequest(this._xmlHttpRequest);
this._xmlHttpRequest.open(this._method, this._url, true); xmlHttpRequest.open(this._method, this._host + this._url, true);
this._xmlHttpRequest.withCredentials = this._withCredentials; let authToken = btoa(basicAuth.username +':'+ basicAuth.password);
if (authToken) {
if (this._authToken) { xmlHttpRequest.setRequestHeader('Authorization', `${basicAuth.type} ${authToken}`);
this._xmlHttpRequest.setRequestHeader('Authorization', `${this._authTokenPrefix} ${this._authToken}`);
} }
this._xmlHttpRequest.send(form); xmlHttpRequest.send(form);
} }
/** /**
@ -169,10 +222,35 @@ export class UploadService {
} }
/** /**
* Set XMLHttpRequest method * Create a folder
* @param xhr * @param name - the folder name
*/ */
public setXMLHttpRequest(xhr: XMLHttpRequest) { createFolder(relativePath: string, name: string) {
this._xmlHttpRequest = xhr; 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');
} }
} }