#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)">
<alfresco-document-list (folderClick)="refreshDirectyory($event)" currentFolderPath="{{absolutePath}}">
<content-columns>
@ -8,6 +8,10 @@
source="name"
class="full-width name-column">
</content-column>
<content-column
title="{{'DOCUMENT_LIST.COLUMNS.SITE' | translate}}"
source="location.site">
</content-column>
<content-column
title="{{'DOCUMENT_LIST.COLUMNS.CREATED_BY' | translate}}"
source="createdByUser.displayName">
@ -74,4 +78,4 @@
</alfresco-document-list>
</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()
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];
}
}

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>
<ng-content></ng-content>
</div>

View File

@ -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];
}
}

View File

@ -43,6 +43,12 @@ export class FileDraggableDirective {
@Output()
onFilesDropped: EventEmitter<any> = new EventEmitter();
@Output()
onFilesEntityDropped: EventEmitter<any> = new EventEmitter();
@Output()
onFolderEntityDropped: EventEmitter<any> = 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);
}
}
}

View File

@ -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');
});
});

View File

@ -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,12 +133,12 @@ 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>) {
if (this._xmlHttpRequest === undefined) {
this._xmlHttpRequest = new XMLHttpRequest();
this._xmlHttpRequest.upload.onprogress = (e) => {
createXMLHttpRequestInstance(uploadingFileModel: any, elementEmit: EventEmitter<any>) {
let xmlHttpRequest = new XMLHttpRequest();
xmlHttpRequest.upload.onprogress = (e) => {
if (e.lengthComputable) {
let percent = Math.round(e.loaded / e.total * 100);
uploadingFileModel.setProgres({
@ -99,27 +149,27 @@ export class UploadService {
}
};
this._xmlHttpRequest.upload.onabort = (e) => {
xmlHttpRequest.upload.onabort = (e) => {
uploadingFileModel.setAbort();
};
this._xmlHttpRequest.upload.onerror = (e) => {
xmlHttpRequest.upload.onerror = (e) => {
uploadingFileModel.setError();
};
this._xmlHttpRequest.onreadystatechange = () => {
if (this._xmlHttpRequest.readyState === XMLHttpRequest.DONE) {
xmlHttpRequest.onreadystatechange = () => {
if (xmlHttpRequest.readyState === XMLHttpRequest.DONE) {
elementEmit.emit({
value: 'File uploaded'
});
uploadingFileModel.onFinished(
this._xmlHttpRequest.status,
this._xmlHttpRequest.statusText,
this._xmlHttpRequest.response
xmlHttpRequest.status,
xmlHttpRequest.statusText,
xmlHttpRequest.response
);
}
};
}
return xmlHttpRequest;
}
/**
@ -129,6 +179,11 @@ export class UploadService {
*
*/
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();
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');
}
}