mirror of
https://github.com/Alfresco/alfresco-ng2-components.git
synced 2025-07-31 17:38:48 +00:00
[ADF-571] upload feature rework (#1922)
* upload feature rework lots of improvements for upload dialog and underlying services * readme update - readme cleanup - remove some old comments from code - update readme with new events for Upload Service * restore prerequisites section in readme
This commit is contained in:
committed by
Eugenio Romano
parent
b4c9710e71
commit
c2fee79724
@@ -17,10 +17,10 @@
|
|||||||
|
|
||||||
import { Component, Input, OnInit, AfterViewInit, Optional, ViewChild, ChangeDetectorRef } from '@angular/core';
|
import { Component, Input, OnInit, AfterViewInit, Optional, ViewChild, ChangeDetectorRef } from '@angular/core';
|
||||||
import { ActivatedRoute, Params, Router } from '@angular/router';
|
import { ActivatedRoute, Params, Router } from '@angular/router';
|
||||||
import { AlfrescoAuthenticationService, LogService, NotificationService } from 'ng2-alfresco-core';
|
import { AlfrescoAuthenticationService, AlfrescoContentService, FolderCreatedEvent, LogService, NotificationService } from 'ng2-alfresco-core';
|
||||||
import { DocumentActionsService, DocumentListComponent, ContentActionHandler, DocumentActionModel, FolderActionModel } from 'ng2-alfresco-documentlist';
|
import { DocumentActionsService, DocumentListComponent, ContentActionHandler, DocumentActionModel, FolderActionModel } from 'ng2-alfresco-documentlist';
|
||||||
import { FormService } from 'ng2-activiti-form';
|
import { FormService } from 'ng2-activiti-form';
|
||||||
import { UploadService, UploadButtonComponent, UploadDragAreaComponent, FolderCreatedEvent } from 'ng2-alfresco-upload';
|
import { UploadService, UploadButtonComponent, UploadDragAreaComponent } from 'ng2-alfresco-upload';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'files-component',
|
selector: 'files-component',
|
||||||
@@ -73,6 +73,7 @@ export class FilesComponent implements OnInit, AfterViewInit {
|
|||||||
private router: Router,
|
private router: Router,
|
||||||
private notificationService: NotificationService,
|
private notificationService: NotificationService,
|
||||||
private uploadService: UploadService,
|
private uploadService: UploadService,
|
||||||
|
private contentService: AlfrescoContentService,
|
||||||
@Optional() private route: ActivatedRoute) {
|
@Optional() private route: ActivatedRoute) {
|
||||||
documentActions.setHandler('my-handler', this.myDocumentActionHandler.bind(this));
|
documentActions.setHandler('my-handler', this.myDocumentActionHandler.bind(this));
|
||||||
}
|
}
|
||||||
@@ -122,7 +123,7 @@ export class FilesComponent implements OnInit, AfterViewInit {
|
|||||||
this.logService.warn('You are not logged in to BPM');
|
this.logService.warn('You are not logged in to BPM');
|
||||||
}
|
}
|
||||||
|
|
||||||
this.uploadService.folderCreated.subscribe(value => this.onFolderCreated(value));
|
this.contentService.folderCreated.subscribe(value => this.onFolderCreated(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
ngAfterViewInit() {
|
ngAfterViewInit() {
|
||||||
|
@@ -41,6 +41,7 @@ import {
|
|||||||
ContentService
|
ContentService
|
||||||
} from './src/services/index';
|
} from './src/services/index';
|
||||||
|
|
||||||
|
import { FileSizePipe } from './src/pipes/file-size.pipe';
|
||||||
import { UploadDirective } from './src/directives/upload.directive';
|
import { UploadDirective } from './src/directives/upload.directive';
|
||||||
import { DataColumnComponent } from './src/components/data-column/data-column.component';
|
import { DataColumnComponent } from './src/components/data-column/data-column.component';
|
||||||
import { DataColumnListComponent } from './src/components/data-column/data-column-list.component';
|
import { DataColumnListComponent } from './src/components/data-column/data-column-list.component';
|
||||||
@@ -57,6 +58,7 @@ export * from './src/directives/upload.directive';
|
|||||||
export * from './src/utils/index';
|
export * from './src/utils/index';
|
||||||
export * from './src/events/base.event';
|
export * from './src/events/base.event';
|
||||||
export * from './src/events/base-ui.event';
|
export * from './src/events/base-ui.event';
|
||||||
|
export * from './src/events/folder-created.event';
|
||||||
|
|
||||||
export const ALFRESCO_CORE_PROVIDERS: any[] = [
|
export const ALFRESCO_CORE_PROVIDERS: any[] = [
|
||||||
NotificationService,
|
NotificationService,
|
||||||
@@ -101,7 +103,8 @@ export function createTranslateLoader(http: Http, logService: LogService) {
|
|||||||
...COLLAPSABLE_DIRECTIVES,
|
...COLLAPSABLE_DIRECTIVES,
|
||||||
UploadDirective,
|
UploadDirective,
|
||||||
DataColumnComponent,
|
DataColumnComponent,
|
||||||
DataColumnListComponent
|
DataColumnListComponent,
|
||||||
|
FileSizePipe
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
...ALFRESCO_CORE_PROVIDERS
|
...ALFRESCO_CORE_PROVIDERS
|
||||||
@@ -119,7 +122,8 @@ export function createTranslateLoader(http: Http, logService: LogService) {
|
|||||||
UploadDirective,
|
UploadDirective,
|
||||||
DataColumnComponent,
|
DataColumnComponent,
|
||||||
DataColumnListComponent,
|
DataColumnListComponent,
|
||||||
MdSnackBarModule
|
MdSnackBarModule,
|
||||||
|
FileSizePipe
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
export class CoreModule {
|
export class CoreModule {
|
||||||
|
36
ng2-components/ng2-alfresco-core/src/pipes/file-size.pipe.ts
Normal file
36
ng2-components/ng2-alfresco-core/src/pipes/file-size.pipe.ts
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
/*!
|
||||||
|
* @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 { Pipe, PipeTransform } from '@angular/core';
|
||||||
|
|
||||||
|
@Pipe({
|
||||||
|
name: 'adfFileSize'
|
||||||
|
})
|
||||||
|
export class FileSizePipe implements PipeTransform {
|
||||||
|
|
||||||
|
transform(bytes: number = 0, decimals: number = 2): string {
|
||||||
|
if (bytes === 0) {
|
||||||
|
return '0 Bytes';
|
||||||
|
}
|
||||||
|
const k = 1024,
|
||||||
|
dm = decimals || 2,
|
||||||
|
sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'],
|
||||||
|
i = Math.floor(Math.log(bytes) / Math.log(k));
|
||||||
|
return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -16,15 +16,18 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
|
import { Observable, Subject } from 'rxjs/Rx';
|
||||||
|
import { MinimalNodeEntity } from 'alfresco-js-api';
|
||||||
import { AlfrescoAuthenticationService } from './alfresco-authentication.service';
|
import { AlfrescoAuthenticationService } from './alfresco-authentication.service';
|
||||||
import { AlfrescoApiService } from './alfresco-api.service';
|
import { AlfrescoApiService } from './alfresco-api.service';
|
||||||
import { LogService } from './log.service.ts';
|
import { LogService } from './log.service';
|
||||||
import { Observable } from 'rxjs/Rx';
|
import { FolderCreatedEvent } from '../events/folder-created.event';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class AlfrescoContentService {
|
export class AlfrescoContentService {
|
||||||
|
|
||||||
|
folderCreated: Subject<FolderCreatedEvent> = new Subject<FolderCreatedEvent>();
|
||||||
|
|
||||||
constructor(public authService: AlfrescoAuthenticationService,
|
constructor(public authService: AlfrescoAuthenticationService,
|
||||||
public apiService: AlfrescoApiService,
|
public apiService: AlfrescoApiService,
|
||||||
private logService: LogService) {
|
private logService: LogService) {
|
||||||
@@ -70,6 +73,23 @@ export class AlfrescoContentService {
|
|||||||
})).catch(this.handleError);
|
})).catch(this.handleError);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a folder
|
||||||
|
* @param name - the folder name
|
||||||
|
*/
|
||||||
|
createFolder(relativePath: string, name: string, parentId?: string): Observable<MinimalNodeEntity> {
|
||||||
|
return Observable.fromPromise(this.apiService.getInstance().nodes.createFolder(name, relativePath, parentId))
|
||||||
|
.do(data => {
|
||||||
|
this.folderCreated.next({
|
||||||
|
relativePath: relativePath,
|
||||||
|
name: name,
|
||||||
|
parentId: parentId,
|
||||||
|
node: data
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch(err => this.handleError(err));
|
||||||
|
}
|
||||||
|
|
||||||
private handleError(error: any) {
|
private handleError(error: any) {
|
||||||
this.logService.error(error);
|
this.logService.error(error);
|
||||||
return Observable.throw(error || 'Server error');
|
return Observable.throw(error || 'Server error');
|
||||||
|
@@ -18,20 +18,25 @@
|
|||||||
<a href='https://github.com/Alfresco/alfresco-ng2-components/blob/master/LICENSE'>
|
<a href='https://github.com/Alfresco/alfresco-ng2-components/blob/master/LICENSE'>
|
||||||
<img src='https://img.shields.io/hexpm/l/plug.svg' alt='license' />
|
<img src='https://img.shields.io/hexpm/l/plug.svg' alt='license' />
|
||||||
</a>
|
</a>
|
||||||
<a href='https://www.alfresco.com/'>
|
|
||||||
<img src='https://img.shields.io/badge/style-component-green.svg?label=alfresco' alt='alfresco component' />
|
|
||||||
</a>
|
|
||||||
<a href='https://angular.io/'>
|
|
||||||
<img src='https://img.shields.io/badge/style-2-red.svg?label=angular' alt='angular 2' />
|
|
||||||
</a>
|
|
||||||
<a href='https://www.typescriptlang.org/docs/tutorial.html'>
|
|
||||||
<img src='https://img.shields.io/badge/style-lang-blue.svg?label=typescript' alt='typescript' />
|
|
||||||
</a>
|
|
||||||
<a href='https://www.alfresco.com/'>
|
|
||||||
<img src='https://img.shields.io/badge/style-%3E5.0.0-blue.svg?label=node%20version' alt='node version' />
|
|
||||||
</a>
|
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
## Content
|
||||||
|
|
||||||
|
### Components
|
||||||
|
|
||||||
|
- [FileUploadingDialogComponent](#fileuploadingdialogcomponent)
|
||||||
|
- FileUploadingListComponent
|
||||||
|
- [UploadButtonComponent](#uploadbuttoncomponent)
|
||||||
|
- [UploadDragAreaComponent](#uploaddragareacomponent)
|
||||||
|
|
||||||
|
### Services
|
||||||
|
|
||||||
|
- [UploadService](#uploadservice)
|
||||||
|
|
||||||
|
### Directives
|
||||||
|
|
||||||
|
- FileDraggableDirective
|
||||||
|
|
||||||
## Prerequisites
|
## Prerequisites
|
||||||
|
|
||||||
Before you start using this development framework, make sure you have installed all required software and done all the
|
Before you start using this development framework, make sure you have installed all required software and done all the
|
||||||
@@ -93,19 +98,17 @@ Follow the 3 steps below:
|
|||||||
|
|
||||||
Please refer to the following example file: [systemjs.config.js](demo/systemjs.config.js) .
|
Please refer to the following example file: [systemjs.config.js](demo/systemjs.config.js) .
|
||||||
|
|
||||||
|
## UploadButtonComponent
|
||||||
|
|
||||||
#### Basic usage
|
|
||||||
|
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<alfresco-upload-button [showNotificationBar]="true"
|
<alfresco-upload-button
|
||||||
[uploadFolders]="true"
|
[showNotificationBar]="true"
|
||||||
[multipleFiles]="false"
|
[uploadFolders]="true"
|
||||||
[acceptedFilesType]=".jpg,.gif,.png,.svg"
|
[multipleFiles]="false"
|
||||||
[currentFolderPath]="/Sites/swsdp/documentLibrary"
|
[acceptedFilesType]=".jpg,.gif,.png,.svg"
|
||||||
[versioning]="false"
|
[currentFolderPath]="/Sites/swsdp/documentLibrary"
|
||||||
(onSuccess)="customMethod($event)">
|
[versioning]="false"
|
||||||
|
(onSuccess)="customMethod($event)">
|
||||||
</alfresco-upload-button>
|
</alfresco-upload-button>
|
||||||
<file-uploading-dialog></file-uploading-dialog>
|
<file-uploading-dialog></file-uploading-dialog>
|
||||||
```
|
```
|
||||||
@@ -121,26 +124,27 @@ import { UploadModule } from 'ng2-alfresco-upload';
|
|||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'alfresco-app-demo',
|
selector: 'alfresco-app-demo',
|
||||||
template: `<alfresco-upload-button [showNotificationBar]="true"
|
template: `
|
||||||
[uploadFolders]="false"
|
<alfresco-upload-button
|
||||||
[multipleFiles]="false"
|
[showNotificationBar]="true"
|
||||||
[acceptedFilesType]="'.jpg,.gif,.png,.svg'"
|
[uploadFolders]="false"
|
||||||
(onSuccess)="onSuccess($event)">
|
[multipleFiles]="false"
|
||||||
</alfresco-upload-button>
|
[acceptedFilesType]="'.jpg,.gif,.png,.svg'"
|
||||||
<file-uploading-dialog></file-uploading-dialog>`
|
(onSuccess)="onSuccess($event)">
|
||||||
|
</alfresco-upload-button>
|
||||||
|
<file-uploading-dialog></file-uploading-dialog>
|
||||||
|
`
|
||||||
})
|
})
|
||||||
export class MyDemoApp {
|
export class MyDemoApp {
|
||||||
|
|
||||||
constructor(private authService: AlfrescoAuthenticationService, private settingsService: AlfrescoSettingsService) {
|
constructor(private authService: AlfrescoAuthenticationService,
|
||||||
|
private settingsService: AlfrescoSettingsService) {
|
||||||
settingsService.ecmHost = 'http://localhost:8080';
|
settingsService.ecmHost = 'http://localhost:8080';
|
||||||
|
|
||||||
this.authService.login('admin', 'admin').subscribe(
|
this.authService.login('admin', 'admin').subscribe(
|
||||||
ticket => {
|
ticket => console.log(ticket),
|
||||||
console.log(ticket);
|
error => console.log(error)
|
||||||
},
|
);
|
||||||
error => {
|
|
||||||
console.log(error);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public onSuccess(event: Object): void {
|
public onSuccess(event: Object): void {
|
||||||
@@ -160,15 +164,15 @@ export class MyDemoApp {
|
|||||||
export class AppModule { }
|
export class AppModule { }
|
||||||
|
|
||||||
platformBrowserDynamic().bootstrapModule(AppModule);
|
platformBrowserDynamic().bootstrapModule(AppModule);
|
||||||
|
|
||||||
```
|
```
|
||||||
#### Events
|
|
||||||
|
### Events
|
||||||
|
|
||||||
| Name | Description |
|
| Name | Description |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| `onSuccess` | The event is emitted when the file is uploaded |
|
| `onSuccess` | The event is emitted when the file is uploaded |
|
||||||
|
|
||||||
#### Properties
|
### Properties
|
||||||
|
|
||||||
| Name | Type | Default | Description |
|
| Name | Type | Default | Description |
|
||||||
| --- | --- | --- | --- |
|
| --- | --- | --- | --- |
|
||||||
@@ -182,7 +186,10 @@ platformBrowserDynamic().bootstrapModule(AppModule);
|
|||||||
| `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 |
|
| `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 |
|
| `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 |
|
||||||
|
|
||||||
### How to show notification message with no permission
|
### Advanced usage
|
||||||
|
|
||||||
|
#### How to show notification message with no permission
|
||||||
|
|
||||||
You can show a notification error when the user doesn't have the right permission to perform the action.
|
You can show a notification error when the user doesn't have the right permission to perform the action.
|
||||||
The UploadButtonComponent provides the event permissionEvent that is raised when the delete permission is missing
|
The UploadButtonComponent provides the event permissionEvent that is raised when the delete permission is missing
|
||||||
You can subscribe to this event from your component and use the NotificationService to show a message.
|
You can subscribe to this event from your component and use the NotificationService to show a message.
|
||||||
@@ -195,9 +202,11 @@ You can subscribe to this event from your component and use the NotificationServ
|
|||||||
|
|
||||||
export class MyComponent {
|
export class MyComponent {
|
||||||
|
|
||||||
onUploadPermissionFailed(event: any) {
|
onUploadPermissionFailed(event: any) {
|
||||||
this.notificationService.openSnackMessage(`you don't have the ${event.permission} permission to ${event.action} the ${event.type} `, 4000);
|
this.notificationService.openSnackMessage(
|
||||||
}
|
`you don't have the ${event.permission} permission to ${event.action} the ${event.type} `, 4000
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -205,6 +214,7 @@ onUploadPermissionFailed(event: any) {
|
|||||||

|

|
||||||
|
|
||||||
#### How to disable the button when the delete permission is missing
|
#### How to disable the button when the delete permission is missing
|
||||||
|
|
||||||
You can easily disable the button when the user doesn't own the permission to perform the action.
|
You can easily disable the button when the user doesn't own the permission to perform the action.
|
||||||
The UploadButtonComponent provides the property disableWithNoPermission that can be true. In this way the button should be disabled if the delete permission is missing for the node.
|
The UploadButtonComponent provides the property disableWithNoPermission that can be true. In this way the button should be disabled if the delete permission is missing for the node.
|
||||||
|
|
||||||
@@ -217,19 +227,18 @@ The UploadButtonComponent provides the property disableWithNoPermission that can
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
## UploadDragAreaComponent
|
||||||
|
|
||||||
|
|
||||||
### Drag and drop
|
|
||||||
This component, provide a drag and drop are to upload files to alfresco.
|
This component, provide a drag and drop are to upload files to alfresco.
|
||||||
|
|
||||||
#### Basic usage
|
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<alfresco-upload-drag-area (onSuccess)="customMethod($event)"></alfresco-upload-drag-area>
|
<alfresco-upload-drag-area
|
||||||
|
(onSuccess)="customMethod($event)">
|
||||||
|
</alfresco-upload-drag-area>
|
||||||
<file-uploading-dialog></file-uploading-dialog>
|
<file-uploading-dialog></file-uploading-dialog>
|
||||||
```
|
```
|
||||||
|
|
||||||
Example of an App that declares upload drag and drop component :
|
Example of an App that declares upload drag and drop component:
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
import { NgModule, Component } from '@angular/core';
|
import { NgModule, Component } from '@angular/core';
|
||||||
@@ -240,25 +249,25 @@ import { UploadModule } from 'ng2-alfresco-upload';
|
|||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'alfresco-app-demo',
|
selector: 'alfresco-app-demo',
|
||||||
template: `<alfresco-upload-drag-area (onSuccess)="customMethod($event)" >
|
template: `
|
||||||
<div style="width: 200px; height: 100px; border: 1px solid #888888">
|
<alfresco-upload-drag-area (onSuccess)="customMethod($event)" >
|
||||||
DRAG HERE
|
<div style="width: 200px; height: 100px; border: 1px solid #888888">
|
||||||
</div>
|
DRAG HERE
|
||||||
</alfresco-upload-drag-area>
|
</div>
|
||||||
<file-uploading-dialog></file-uploading-dialog>`
|
</alfresco-upload-drag-area>
|
||||||
|
<file-uploading-dialog></file-uploading-dialog>
|
||||||
|
`
|
||||||
})
|
})
|
||||||
export class MyDemoApp {
|
export class MyDemoApp {
|
||||||
|
|
||||||
constructor(private authService: AlfrescoAuthenticationService, private settingsService: AlfrescoSettingsService) {
|
constructor(private authService: AlfrescoAuthenticationService,
|
||||||
|
private settingsService: AlfrescoSettingsService) {
|
||||||
settingsService.ecmHost = 'http://localhost:8080';
|
settingsService.ecmHost = 'http://localhost:8080';
|
||||||
|
|
||||||
this.authService.login('admin', 'admin').subscribe(
|
this.authService.login('admin', 'admin').subscribe(
|
||||||
ticket => {
|
ticket => console.log(ticket),
|
||||||
console.log(ticket);
|
error => console.log(error)
|
||||||
},
|
);
|
||||||
error => {
|
|
||||||
console.log(error);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public onSuccess(event: Object): void {
|
public onSuccess(event: Object): void {
|
||||||
@@ -278,16 +287,15 @@ export class MyDemoApp {
|
|||||||
export class AppModule { }
|
export class AppModule { }
|
||||||
|
|
||||||
platformBrowserDynamic().bootstrapModule(AppModule);
|
platformBrowserDynamic().bootstrapModule(AppModule);
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Events
|
### Events
|
||||||
|
|
||||||
| Name | Description |
|
| Name | Description |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| `onSuccess` | The event is emitted when the file is uploaded |
|
| `onSuccess` | The event is emitted when the file is uploaded |
|
||||||
|
|
||||||
#### Properties
|
### Properties
|
||||||
|
|
||||||
| Name | Type | Default | Description |
|
| Name | Type | Default | Description |
|
||||||
| --- | --- | --- | --- |
|
| --- | --- | --- | --- |
|
||||||
@@ -297,27 +305,31 @@ platformBrowserDynamic().bootstrapModule(AppModule);
|
|||||||
| `currentFolderPath` | *string* | '/' | define the path where the files are uploaded |
|
| `currentFolderPath` | *string* | '/' | define the path where the files are uploaded |
|
||||||
| `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 |
|
| `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
|
||||||
### Files Dialog
|
|
||||||
|
|
||||||
This component provides a dialog that shows all the files uploaded with upload button or drag & drop area components.
|
This component provides a dialog that shows all the files uploaded with upload button or drag & drop area components.
|
||||||
This component should be used in combination with upload button or drag & drop area.
|
This component should be used in combination with upload button or drag & drop area.
|
||||||
|
|
||||||
#### Basic usage
|
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<file-uploading-dialog></file-uploading-dialog>
|
<file-uploading-dialog></file-uploading-dialog>
|
||||||
```
|
```
|
||||||
|
|
||||||
### UploadService service
|
## UploadService
|
||||||
|
|
||||||
Provides access to various APIs related to file upload features.
|
Provides access to various APIs related to file upload features.
|
||||||
|
|
||||||
#### Events
|
### Events
|
||||||
|
|
||||||
| Name | Type | Description |
|
| Name | Type | Description |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| folderCreated | FolderCreatedEvent | Raised when dropped folder gets created |
|
| queueChanged | FileModel[] | Raised every time the file queue changes. |
|
||||||
|
| fileUpload | FileUploadEvent | Raised every time a File model changes its state. |
|
||||||
|
| fileUploadStarting | FileUploadEvent | Raised when upload starts. |
|
||||||
|
| fileUploadCancelled | FileUploadEvent | Raised when upload gets cancelled by user. |
|
||||||
|
| fileUploadProgress | FileUploadEvent | Raised during file upload process and contains the current progress for the particular File model. |
|
||||||
|
| fileUploadAborted | FileUploadEvent | Raised when file upload gets aborted by the server. |
|
||||||
|
| fileUploadError | FileUploadEvent | Raised when an error occurs to file upload. |
|
||||||
|
| fileUploadComplete | FileUploadCompleteEvent | Raised when file upload is complete. |
|
||||||
|
|
||||||
## Build from sources
|
## Build from sources
|
||||||
|
|
||||||
@@ -331,7 +343,7 @@ npm run build
|
|||||||
### Build the files and keep watching for changes
|
### Build the files and keep watching for changes
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
$ npm run build:w
|
npm run build:w
|
||||||
```
|
```
|
||||||
|
|
||||||
## Running unit tests
|
## Running unit tests
|
||||||
|
@@ -16,7 +16,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { NgModule, ModuleWithProviders } from '@angular/core';
|
import { NgModule, ModuleWithProviders } from '@angular/core';
|
||||||
import { MdIconModule } from '@angular/material';
|
import { MdIconModule, MdProgressSpinnerModule, MdButtonModule } from '@angular/material';
|
||||||
import { CoreModule } from 'ng2-alfresco-core';
|
import { CoreModule } from 'ng2-alfresco-core';
|
||||||
|
|
||||||
import { UploadDragAreaComponent } from './src/components/upload-drag-area.component';
|
import { UploadDragAreaComponent } from './src/components/upload-drag-area.component';
|
||||||
@@ -26,22 +26,6 @@ import { FileUploadingDialogComponent } from './src/components/file-uploading-di
|
|||||||
import { FileUploadingListComponent } from './src/components/file-uploading-list.component';
|
import { FileUploadingListComponent } from './src/components/file-uploading-list.component';
|
||||||
import { UploadService } from './src/services/upload.service';
|
import { UploadService } from './src/services/upload.service';
|
||||||
|
|
||||||
/**
|
|
||||||
* ng2-alfresco-upload, provide components to upload files to alfresco repository.
|
|
||||||
*
|
|
||||||
* Components provided:
|
|
||||||
* - A button to upload files
|
|
||||||
* <alfresco-upload-button [showDialogUpload]="boolean"
|
|
||||||
* [showNotificationBar]="boolean"
|
|
||||||
* [uploadFolders]="boolean"
|
|
||||||
* [multipleFiles]="boolean"
|
|
||||||
* [acceptedFilesType]="string">
|
|
||||||
* </alfresco-upload-button>
|
|
||||||
*
|
|
||||||
* - Drag and drop area to upload files:
|
|
||||||
* <alfresco-upload-drag-area [showDialogUpload]="boolean" ></alfresco-upload-drag-area>
|
|
||||||
*/
|
|
||||||
|
|
||||||
export * from './src/components/upload-button.component';
|
export * from './src/components/upload-button.component';
|
||||||
export * from './src/components/file-uploading-dialog.component';
|
export * from './src/components/file-uploading-dialog.component';
|
||||||
export * from './src/components/upload-drag-area.component';
|
export * from './src/components/upload-drag-area.component';
|
||||||
@@ -50,7 +34,7 @@ export * from './src/directives/file-draggable.directive';
|
|||||||
export * from './src/components/file-uploading-list.component';
|
export * from './src/components/file-uploading-list.component';
|
||||||
export * from './src/models/file.model';
|
export * from './src/models/file.model';
|
||||||
export * from './src/models/permissions.model';
|
export * from './src/models/permissions.model';
|
||||||
export * from './src/events/folder-created.event';
|
export * from './src/events/file.event';
|
||||||
|
|
||||||
export const UPLOAD_DIRECTIVES: any[] = [
|
export const UPLOAD_DIRECTIVES: any[] = [
|
||||||
FileDraggableDirective,
|
FileDraggableDirective,
|
||||||
@@ -67,7 +51,9 @@ export const UPLOAD_PROVIDERS: any[] = [
|
|||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [
|
||||||
CoreModule,
|
CoreModule,
|
||||||
MdIconModule
|
MdIconModule,
|
||||||
|
MdProgressSpinnerModule,
|
||||||
|
MdButtonModule
|
||||||
],
|
],
|
||||||
declarations: [
|
declarations: [
|
||||||
...UPLOAD_DIRECTIVES
|
...UPLOAD_DIRECTIVES
|
||||||
|
@@ -14,7 +14,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
:host .file-dialog {
|
:host .file-dialog {
|
||||||
width: 700px;
|
width: 550px;
|
||||||
display: none;
|
display: none;
|
||||||
-webkit-box-shadow: 0 2px 8px 0 rgba(0, 0, 0, .2);
|
-webkit-box-shadow: 0 2px 8px 0 rgba(0, 0, 0, .2);
|
||||||
box-shadow: -2px -1px 8px 3px rgba(0, 0, 0, .2);
|
box-shadow: -2px -1px 8px 3px rgba(0, 0, 0, .2);
|
||||||
|
@@ -16,12 +16,14 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { DebugElement } from '@angular/core';
|
import { DebugElement } from '@angular/core';
|
||||||
|
import { MdProgressSpinnerModule } from '@angular/material';
|
||||||
import { ComponentFixture, TestBed, async } from '@angular/core/testing';
|
import { ComponentFixture, TestBed, async } from '@angular/core/testing';
|
||||||
import { CoreModule } from 'ng2-alfresco-core';
|
import { CoreModule } from 'ng2-alfresco-core';
|
||||||
import { FileUploadingDialogComponent } from './file-uploading-dialog.component';
|
import { FileUploadingDialogComponent } from './file-uploading-dialog.component';
|
||||||
import { FileUploadingListComponent } from './file-uploading-list.component';
|
import { FileUploadingListComponent } from './file-uploading-list.component';
|
||||||
import { UploadService } from '../services/upload.service';
|
import { UploadService } from '../services/upload.service';
|
||||||
import { FileModel } from '../models/file.model';
|
import { FileModel } from '../models/file.model';
|
||||||
|
import { FileUploadCompleteEvent } from '../events/file.event';
|
||||||
|
|
||||||
describe('FileUploadingDialogComponent', () => {
|
describe('FileUploadingDialogComponent', () => {
|
||||||
|
|
||||||
@@ -35,7 +37,8 @@ describe('FileUploadingDialogComponent', () => {
|
|||||||
beforeEach(async(() => {
|
beforeEach(async(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [
|
imports: [
|
||||||
CoreModule.forRoot()
|
CoreModule.forRoot(),
|
||||||
|
MdProgressSpinnerModule
|
||||||
],
|
],
|
||||||
declarations: [
|
declarations: [
|
||||||
FileUploadingDialogComponent,
|
FileUploadingDialogComponent,
|
||||||
@@ -48,8 +51,6 @@ describe('FileUploadingDialogComponent', () => {
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
window['componentHandler'] = null;
|
|
||||||
|
|
||||||
const fileFake = new File([''], 'fake-name');
|
const fileFake = new File([''], 'fake-name');
|
||||||
file = new FileModel(fileFake);
|
file = new FileModel(fileFake);
|
||||||
|
|
||||||
@@ -70,7 +71,7 @@ describe('FileUploadingDialogComponent', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should render completed upload 1 when an element is added to Observer', () => {
|
it('should render completed upload 1 when an element is added to Observer', () => {
|
||||||
uploadService.updateFileCounterStream(1);
|
uploadService.fileUploadComplete.next(new FileUploadCompleteEvent(null, 1));
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
|
||||||
expect(element.querySelector('#total-upload-completed').innerText).toEqual('1');
|
expect(element.querySelector('#total-upload-completed').innerText).toEqual('1');
|
||||||
|
@@ -15,24 +15,15 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Component, Input, ChangeDetectorRef, OnInit, OnDestroy } from '@angular/core';
|
import { Component, Input, ChangeDetectorRef, OnInit, OnDestroy, ChangeDetectionStrategy } from '@angular/core';
|
||||||
import { FileModel } from '../models/file.model';
|
import { FileModel } from '../models/file.model';
|
||||||
import { AlfrescoTranslationService } from 'ng2-alfresco-core';
|
import { AlfrescoTranslationService } from 'ng2-alfresco-core';
|
||||||
import { UploadService } from '../services/upload.service';
|
import { UploadService } from '../services/upload.service';
|
||||||
|
import { FileUploadCompleteEvent } from '../events/file.event';
|
||||||
|
|
||||||
/**
|
|
||||||
* <file-uploading-dialog [filesUploadingList]="FileModel[]"></file-uploading-dialog>
|
|
||||||
*
|
|
||||||
* This component is a hideable and minimizable wich contains the list of the uploading
|
|
||||||
* files contained in the filesUploadingList.
|
|
||||||
*
|
|
||||||
* @InputParam {FileModel[]} filesUploadingList - list of the uploading files .
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* @returns {FileUploadingDialogComponent} .
|
|
||||||
*/
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'file-uploading-dialog',
|
selector: 'file-uploading-dialog',
|
||||||
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
templateUrl: './file-uploading-dialog.component.html',
|
templateUrl: './file-uploading-dialog.component.html',
|
||||||
styleUrls: ['./file-uploading-dialog.component.css']
|
styleUrls: ['./file-uploading-dialog.component.css']
|
||||||
})
|
})
|
||||||
@@ -55,27 +46,30 @@ export class FileUploadingDialogComponent implements OnInit, OnDestroy {
|
|||||||
if (translateService) {
|
if (translateService) {
|
||||||
translateService.addTranslationFolder('ng2-alfresco-upload', 'assets/ng2-alfresco-upload');
|
translateService.addTranslationFolder('ng2-alfresco-upload', 'assets/ng2-alfresco-upload');
|
||||||
}
|
}
|
||||||
|
cd.detach();
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
if (this.uploadService.filesUpload$) {
|
this.listSubscription = this.uploadService.queueChanged.subscribe((fileList: FileModel[]) => {
|
||||||
this.listSubscription = this.uploadService.filesUpload$.subscribe((fileList: FileModel[]) => {
|
this.filesUploadingList = fileList;
|
||||||
this.filesUploadingList = fileList;
|
if (this.filesUploadingList.length > 0) {
|
||||||
if (this.filesUploadingList.length > 0) {
|
this.isDialogActive = true;
|
||||||
this.isDialogActive = true;
|
|
||||||
this.cd.detectChanges();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (this.uploadService.totalCompleted$) {
|
|
||||||
this.counterSubscription = this.uploadService.totalCompleted$.subscribe((total: number) => {
|
|
||||||
this.totalCompleted = total;
|
|
||||||
if (this.totalCompleted > 1) {
|
|
||||||
this.totalCompletedMsg = 'FILE_UPLOAD.MESSAGES.COMPLETED';
|
|
||||||
}
|
|
||||||
this.cd.detectChanges();
|
this.cd.detectChanges();
|
||||||
});
|
}
|
||||||
}
|
});
|
||||||
|
|
||||||
|
this.counterSubscription = this.uploadService.fileUploadComplete.subscribe((e: FileUploadCompleteEvent) => {
|
||||||
|
this.totalCompleted = e.totalComplete;
|
||||||
|
if (this.totalCompleted > 1) {
|
||||||
|
this.totalCompletedMsg = 'FILE_UPLOAD.MESSAGES.COMPLETED';
|
||||||
|
}
|
||||||
|
this.cd.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
this.uploadService.fileUpload.subscribe(e => {
|
||||||
|
console.log(e);
|
||||||
|
this.cd.detectChanges();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -83,6 +77,7 @@ export class FileUploadingDialogComponent implements OnInit, OnDestroy {
|
|||||||
*/
|
*/
|
||||||
toggleVisible(): void {
|
toggleVisible(): void {
|
||||||
this.isDialogActive = !this.isDialogActive;
|
this.isDialogActive = !this.isDialogActive;
|
||||||
|
this.cd.detectChanges();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -90,11 +85,11 @@ export class FileUploadingDialogComponent implements OnInit, OnDestroy {
|
|||||||
*/
|
*/
|
||||||
toggleMinimized(): void {
|
toggleMinimized(): void {
|
||||||
this.isDialogMinimized = !this.isDialogMinimized;
|
this.isDialogMinimized = !this.isDialogMinimized;
|
||||||
|
this.cd.detectChanges();
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnDestroy() {
|
ngOnDestroy() {
|
||||||
this.listSubscription.unsubscribe();
|
this.listSubscription.unsubscribe();
|
||||||
this.counterSubscription.unsubscribe();
|
this.counterSubscription.unsubscribe();
|
||||||
this.cd.detach();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -3,8 +3,8 @@
|
|||||||
border: 0px;
|
border: 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cursor {
|
.center {
|
||||||
cursor: pointer;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.body-dialog-header {
|
.body-dialog-header {
|
||||||
@@ -42,62 +42,40 @@
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
:host .truncate {
|
.cancel-upload-button {
|
||||||
margin-left: 0;
|
cursor: pointer;
|
||||||
white-space: nowrap;
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
:host .mdl-progress {
|
.file-progress-spinner {
|
||||||
width: 150px;
|
height: 24px;
|
||||||
}
|
width: 100%;
|
||||||
|
text-align: center;
|
||||||
@media (max-device-width: 360px) {
|
|
||||||
.truncate {
|
|
||||||
max-width: 50px;
|
|
||||||
margin-left: 0px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-device-width: 568px) {
|
|
||||||
.truncate {
|
|
||||||
width: 60px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mdl-progress {
|
|
||||||
width: 60px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 740px) {
|
|
||||||
.truncate {
|
|
||||||
max-width: 80px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mdl-progress {
|
|
||||||
max-width: 70px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.size-column {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (min-width: 740px) {
|
|
||||||
.truncate {
|
|
||||||
width: 249px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.size-column {
|
|
||||||
display: table-cell;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.full-width {
|
.full-width {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.no-width {
|
.ellipsis-cell .cell-container {
|
||||||
width: 0%;
|
height: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* visible content */
|
||||||
|
.ellipsis-cell .cell-value {
|
||||||
|
display: block;
|
||||||
|
position: absolute;
|
||||||
|
max-width: 100%;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
line-height: 1em; /* for vertical align of text */
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* cell stretching content */
|
||||||
|
.ellipsis-cell > div:after {
|
||||||
|
content: attr(title);
|
||||||
|
overflow: hidden;
|
||||||
|
height: 0;
|
||||||
|
display: block;
|
||||||
}
|
}
|
||||||
|
@@ -6,32 +6,37 @@
|
|||||||
</div>
|
</div>
|
||||||
<table class="mdl-data-table mdl-js-data-table mdl-shadow--2dp">
|
<table class="mdl-data-table mdl-js-data-table mdl-shadow--2dp">
|
||||||
<tr>
|
<tr>
|
||||||
<th class="mdl-data-table__cell--non-numeric">{{'FILE_UPLOAD.FILE_INFO.NAME' | translate}}</th>
|
<th class="mdl-data-table__cell--non-numeric full-width">{{'ADF_FILE_UPLOAD.FILE_LIST.NAME' | translate}}</th>
|
||||||
<th class="mdl-data-table__cell--non-numeric">{{'FILE_UPLOAD.FILE_INFO.PROGRESS' | translate}}</th>
|
<th class="mdl-data-table__cell center">{{'ADF_FILE_UPLOAD.FILE_LIST.PROGRESS' | translate}}</th>
|
||||||
<th class="mdl-data-table__cell--non-numeric mdl-cell--hide-phone size-column">{{'FILE_UPLOAD.FILE_INFO.SIZE' | translate}}</th>
|
<th class="mdl-data-table__cell mdl-cell--hide-phone size-column center">{{'ADF_FILE_UPLOAD.FILE_LIST.SIZE' | translate}}</th>
|
||||||
<th class="mdl-data-table__cell--non-numeric">{{'FILE_UPLOAD.FILE_INFO.ACTION' | translate}}</th>
|
<th class="mdl-data-table__cell center">{{'ADF_FILE_UPLOAD.FILE_LIST.ACTION' | translate}}</th>
|
||||||
</tr>
|
</tr>
|
||||||
<tr *ngFor="let file of files" tabindex="0">
|
<tr *ngFor="let file of files" tabindex="0">
|
||||||
<td class="mdl-data-table__cell--non-numeric" attr.data-automation-id="dialog_{{file.name}}">
|
<td class="mdl-data-table__cell--non-numeric full-width ellipsis-cell" attr.data-automation-id="dialog_{{file.name}}">
|
||||||
<div class="truncate">{{file.name}}</div>
|
<div class="cell-container">
|
||||||
</td>
|
<div class="cell-value" [title]="file.name">{{file.name}}</div>
|
||||||
<td class="mdl-data-table__cell--non-numeric">
|
|
||||||
<div class="mdl-progress mdl-js-progress is-upgraded" id="{{file.id}}">
|
|
||||||
<div class="progressbar bar bar1" attr.data-automation-id="dialog_progress_{{file.name}}" [style.width.%]="file.progress.percent"></div>
|
|
||||||
<div class="bufferbar bar bar2" class="full-width"></div>
|
|
||||||
<div class="auxbar bar bar3" class="no-width"></div>
|
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td class="mdl-data-table__cell--non-numeric mdl-cell--hide-phone size-column" attr.data-automation-id="{{file.name}}_filesize">{{file.size}}</td>
|
<td class="mdl-data-table__cell center">
|
||||||
<td class="mdl-data-table__cell--non-numeric">
|
<md-icon *ngIf="file.status === FileUploadStatus.Error || file.status === FileUploadStatus.Aborted">error_outline</md-icon>
|
||||||
<span *ngIf="file.done && !file.abort">
|
<md-icon *ngIf="file.status === FileUploadStatus.Cancelled">block</md-icon>
|
||||||
<i data-automation-id="done_icon" class="material-icons action-icons">done</i>
|
<ng-container *ngIf="file.status === FileUploadStatus.Progress">
|
||||||
|
<md-progress-spinner
|
||||||
|
class="file-progress-spinner"
|
||||||
|
[mode]="'determinate'"
|
||||||
|
[value]="file.progress.percent">
|
||||||
|
</md-progress-spinner>
|
||||||
|
</ng-container>
|
||||||
|
</td>
|
||||||
|
<td class="mdl-data-table__cell mdl-cell--hide-phone size-column center" attr.data-automation-id="{{file.name}}_filesize">
|
||||||
|
{{ file.size | adfFileSize }}
|
||||||
|
</td>
|
||||||
|
<td class="mdl-data-table__cell center">
|
||||||
|
<span *ngIf="file.status === FileUploadStatus.Complete">
|
||||||
|
<md-icon>done</md-icon>
|
||||||
</span>
|
</span>
|
||||||
<span *ngIf="file.uploading" (click)="cancelFileUpload(file)" class="cursor" tabindex="0">
|
<span *ngIf="file.status === FileUploadStatus.Progress" (click)="cancelFileUpload(file)" tabindex="0" class="cancel-upload-button">
|
||||||
<i data-automation-id="abort_cancel_upload" class="material-icons action-icons">remove_circle_outline</i>
|
<md-icon>remove_circle_outline</md-icon>
|
||||||
</span>
|
|
||||||
<span *ngIf="file.abort">
|
|
||||||
<i class="material-icons action-icons" data-automation-id="upload_stopped" tabindex="0">remove_circle</i>
|
|
||||||
</span>
|
</span>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
@@ -16,18 +16,9 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { Component, Input } from '@angular/core';
|
import { Component, Input } from '@angular/core';
|
||||||
import { FileModel } from '../models/file.model';
|
import { FileModel, FileUploadStatus } from '../models/file.model';
|
||||||
|
import { UploadService } from '../services/upload.service';
|
||||||
|
|
||||||
/**
|
|
||||||
* <alfresco-file-uploading-list [files]="files"></alfresco-file-uploading-list>
|
|
||||||
*
|
|
||||||
* This component show a list of the uploading files contained in the filesUploadingList.
|
|
||||||
*
|
|
||||||
* @InputParam {FileModel[]} filesUploadingList - list of the uploading files .
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* @returns {FileUploadingListComponent} .
|
|
||||||
*/
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'alfresco-file-uploading-list',
|
selector: 'alfresco-file-uploading-list',
|
||||||
templateUrl: './file-uploading-list.component.html',
|
templateUrl: './file-uploading-list.component.html',
|
||||||
@@ -35,9 +26,14 @@ import { FileModel } from '../models/file.model';
|
|||||||
})
|
})
|
||||||
export class FileUploadingListComponent {
|
export class FileUploadingListComponent {
|
||||||
|
|
||||||
|
FileUploadStatus = FileUploadStatus;
|
||||||
|
|
||||||
@Input()
|
@Input()
|
||||||
files: FileModel[];
|
files: FileModel[];
|
||||||
|
|
||||||
|
constructor(private uploadService: UploadService) {
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cancel file upload
|
* Cancel file upload
|
||||||
*
|
*
|
||||||
@@ -46,9 +42,7 @@ export class FileUploadingListComponent {
|
|||||||
* @memberOf FileUploadingListComponent
|
* @memberOf FileUploadingListComponent
|
||||||
*/
|
*/
|
||||||
cancelFileUpload(file: FileModel): void {
|
cancelFileUpload(file: FileModel): void {
|
||||||
if (file) {
|
this.uploadService.cancelUpload(file);
|
||||||
file.emitAbort();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -58,21 +52,20 @@ export class FileUploadingListComponent {
|
|||||||
if (event) {
|
if (event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
}
|
}
|
||||||
this.files.forEach((uploadingFileModel: FileModel) => {
|
this.uploadService.cancelUpload(...this.files);
|
||||||
uploadingFileModel.emitAbort();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Verify if all the files are in state done or abort
|
* Check if all the files are not in the Progress state.
|
||||||
* @returns {boolean} - false if there is a file in progress
|
* @returns {boolean} - false if there is at least one file in Progress
|
||||||
*/
|
*/
|
||||||
isUploadCompleted(): boolean {
|
isUploadCompleted(): boolean {
|
||||||
let isPending = false;
|
let isPending = false;
|
||||||
let isAllCompleted = true;
|
let isAllCompleted = true;
|
||||||
|
|
||||||
for (let i = 0; i < this.files.length && !isPending; i++) {
|
for (let i = 0; i < this.files.length && !isPending; i++) {
|
||||||
let file = this.files[i];
|
let file = this.files[i];
|
||||||
if (!file.done && !file.abort) {
|
if (file.status === FileUploadStatus.Progress) {
|
||||||
isPending = true;
|
isPending = true;
|
||||||
isAllCompleted = false;
|
isAllCompleted = false;
|
||||||
}
|
}
|
||||||
|
@@ -15,10 +15,10 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { DebugElement, SimpleChange } from '@angular/core';
|
||||||
import { ComponentFixture, TestBed, async } from '@angular/core/testing';
|
import { ComponentFixture, TestBed, async } from '@angular/core/testing';
|
||||||
import { UploadButtonComponent } from './upload-button.component';
|
import { UploadButtonComponent } from './upload-button.component';
|
||||||
import { DebugElement, SimpleChange } from '@angular/core';
|
import { CoreModule, AlfrescoTranslationService, AlfrescoContentService} from 'ng2-alfresco-core';
|
||||||
import { CoreModule, AlfrescoTranslationService, NotificationService } from 'ng2-alfresco-core';
|
|
||||||
import { TranslationMock } from '../assets/translation.service.mock';
|
import { TranslationMock } from '../assets/translation.service.mock';
|
||||||
import { UploadService } from '../services/upload.service';
|
import { UploadService } from '../services/upload.service';
|
||||||
import { Observable } from 'rxjs/Rx';
|
import { Observable } from 'rxjs/Rx';
|
||||||
@@ -33,27 +33,6 @@ describe('UploadButtonComponent', () => {
|
|||||||
target: {value: 'fake-name-1'}
|
target: {value: 'fake-name-1'}
|
||||||
};
|
};
|
||||||
|
|
||||||
let fakeResolveRest = {
|
|
||||||
entry: {
|
|
||||||
isFile: false,
|
|
||||||
isFolder: true,
|
|
||||||
name: 'fake-folder1'
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let fakeResolvePromise = new Promise(function (resolve, reject) {
|
|
||||||
resolve(fakeResolveRest);
|
|
||||||
});
|
|
||||||
|
|
||||||
let fakeRejectRest = {
|
|
||||||
response: {
|
|
||||||
body: {
|
|
||||||
error: {
|
|
||||||
statusCode: 409
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let fakeFolderNodeWithoutPermission = {
|
let fakeFolderNodeWithoutPermission = {
|
||||||
allowableOperations: [
|
allowableOperations: [
|
||||||
'update'
|
'update'
|
||||||
@@ -73,15 +52,12 @@ describe('UploadButtonComponent', () => {
|
|||||||
nodeType: 'cm:folder'
|
nodeType: 'cm:folder'
|
||||||
};
|
};
|
||||||
|
|
||||||
let fakeRejectPromise = new Promise(function (resolve, reject) {
|
|
||||||
reject(fakeRejectRest);
|
|
||||||
});
|
|
||||||
|
|
||||||
let component: UploadButtonComponent;
|
let component: UploadButtonComponent;
|
||||||
let fixture: ComponentFixture<UploadButtonComponent>;
|
let fixture: ComponentFixture<UploadButtonComponent>;
|
||||||
let debug: DebugElement;
|
let debug: DebugElement;
|
||||||
let element: HTMLElement;
|
let element: HTMLElement;
|
||||||
let uploadService: UploadService;
|
let uploadService: UploadService;
|
||||||
|
let contentService: AlfrescoContentService;
|
||||||
|
|
||||||
beforeEach(async(() => {
|
beforeEach(async(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
@@ -93,7 +69,6 @@ describe('UploadButtonComponent', () => {
|
|||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
UploadService,
|
UploadService,
|
||||||
NotificationService,
|
|
||||||
{provide: AlfrescoTranslationService, useClass: TranslationMock}
|
{provide: AlfrescoTranslationService, useClass: TranslationMock}
|
||||||
]
|
]
|
||||||
}).compileComponents();
|
}).compileComponents();
|
||||||
@@ -104,6 +79,7 @@ describe('UploadButtonComponent', () => {
|
|||||||
|
|
||||||
fixture = TestBed.createComponent(UploadButtonComponent);
|
fixture = TestBed.createComponent(UploadButtonComponent);
|
||||||
uploadService = TestBed.get(UploadService);
|
uploadService = TestBed.get(UploadService);
|
||||||
|
contentService = TestBed.get(AlfrescoContentService);
|
||||||
|
|
||||||
debug = fixture.debugElement;
|
debug = fixture.debugElement;
|
||||||
element = fixture.nativeElement;
|
element = fixture.nativeElement;
|
||||||
@@ -141,7 +117,7 @@ describe('UploadButtonComponent', () => {
|
|||||||
component.rootFolderId = '-my-';
|
component.rootFolderId = '-my-';
|
||||||
component.disableWithNoPermission = false;
|
component.disableWithNoPermission = false;
|
||||||
|
|
||||||
spyOn(uploadService, 'getFolderNode').and.returnValue(Observable.of(fakeFolderNodeWithoutPermission));
|
spyOn(component, 'getFolderNode').and.returnValue(Observable.of(fakeFolderNodeWithoutPermission));
|
||||||
|
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
|
||||||
@@ -160,7 +136,7 @@ describe('UploadButtonComponent', () => {
|
|||||||
component.rootFolderId = '-my-';
|
component.rootFolderId = '-my-';
|
||||||
component.disableWithNoPermission = true;
|
component.disableWithNoPermission = true;
|
||||||
|
|
||||||
spyOn(uploadService, 'getFolderNode').and.returnValue(Observable.of(fakeFolderNodeWithoutPermission));
|
spyOn(component, 'getFolderNode').and.returnValue(Observable.of(fakeFolderNodeWithoutPermission));
|
||||||
|
|
||||||
component.onFilesAdded(fakeEvent);
|
component.onFilesAdded(fakeEvent);
|
||||||
let compiled = fixture.debugElement.nativeElement;
|
let compiled = fixture.debugElement.nativeElement;
|
||||||
@@ -173,7 +149,7 @@ describe('UploadButtonComponent', () => {
|
|||||||
component.rootFolderId = '-my-';
|
component.rootFolderId = '-my-';
|
||||||
component.disableWithNoPermission = true;
|
component.disableWithNoPermission = true;
|
||||||
|
|
||||||
spyOn(uploadService, 'getFolderNode').and.returnValue(Observable.of(fakeFolderNodeWithPermission));
|
spyOn(component, 'getFolderNode').and.returnValue(Observable.of(fakeFolderNodeWithPermission));
|
||||||
|
|
||||||
component.ngOnChanges({ rootFolderId: new SimpleChange(null, component.rootFolderId, true) });
|
component.ngOnChanges({ rootFolderId: new SimpleChange(null, component.rootFolderId, true) });
|
||||||
component.onFilesAdded(fakeEvent);
|
component.onFilesAdded(fakeEvent);
|
||||||
@@ -187,7 +163,7 @@ describe('UploadButtonComponent', () => {
|
|||||||
component.rootFolderId = '-my-';
|
component.rootFolderId = '-my-';
|
||||||
component.disableWithNoPermission = false;
|
component.disableWithNoPermission = false;
|
||||||
|
|
||||||
spyOn(uploadService, 'getFolderNode').and.returnValue(Observable.of(fakeFolderNodeWithPermission));
|
spyOn(component, 'getFolderNode').and.returnValue(Observable.of(fakeFolderNodeWithPermission));
|
||||||
|
|
||||||
component.ngOnChanges({ rootFolderId: new SimpleChange(null, component.rootFolderId, true) });
|
component.ngOnChanges({ rootFolderId: new SimpleChange(null, component.rootFolderId, true) });
|
||||||
component.onFilesAdded(fakeEvent);
|
component.onFilesAdded(fakeEvent);
|
||||||
@@ -202,7 +178,7 @@ describe('UploadButtonComponent', () => {
|
|||||||
component.currentFolderPath = '/root-fake-/sites-fake/folder-fake';
|
component.currentFolderPath = '/root-fake-/sites-fake/folder-fake';
|
||||||
component.onSuccess = null;
|
component.onSuccess = null;
|
||||||
|
|
||||||
spyOn(uploadService, 'getFolderNode').and.returnValue(Observable.of(fakeFolderNodeWithPermission));
|
spyOn(component, 'getFolderNode').and.returnValue(Observable.of(fakeFolderNodeWithPermission));
|
||||||
|
|
||||||
component.ngOnChanges({ rootFolderId: new SimpleChange(null, component.rootFolderId, true) });
|
component.ngOnChanges({ rootFolderId: new SimpleChange(null, component.rootFolderId, true) });
|
||||||
uploadService.uploadFilesInTheQueue = jasmine.createSpy('uploadFilesInTheQueue');
|
uploadService.uploadFilesInTheQueue = jasmine.createSpy('uploadFilesInTheQueue');
|
||||||
@@ -218,7 +194,7 @@ describe('UploadButtonComponent', () => {
|
|||||||
component.rootFolderId = '-my-';
|
component.rootFolderId = '-my-';
|
||||||
component.onSuccess = null;
|
component.onSuccess = null;
|
||||||
|
|
||||||
spyOn(uploadService, 'getFolderNode').and.returnValue(Observable.of(fakeFolderNodeWithPermission));
|
spyOn(component, 'getFolderNode').and.returnValue(Observable.of(fakeFolderNodeWithPermission));
|
||||||
component.ngOnChanges({ rootFolderId: new SimpleChange(null, component.rootFolderId, true) });
|
component.ngOnChanges({ rootFolderId: new SimpleChange(null, component.rootFolderId, true) });
|
||||||
|
|
||||||
uploadService.uploadFilesInTheQueue = jasmine.createSpy('uploadFilesInTheQueue');
|
uploadService.uploadFilesInTheQueue = jasmine.createSpy('uploadFilesInTheQueue');
|
||||||
@@ -233,11 +209,12 @@ describe('UploadButtonComponent', () => {
|
|||||||
component.rootFolderId = '-my-';
|
component.rootFolderId = '-my-';
|
||||||
component.currentFolderPath = '/fake-root-path';
|
component.currentFolderPath = '/fake-root-path';
|
||||||
|
|
||||||
spyOn(uploadService, 'getFolderNode').and.returnValue(Observable.of(fakeFolderNodeWithPermission));
|
spyOn(contentService, 'createFolder').and.returnValue(Observable.of(true));
|
||||||
spyOn(uploadService, 'callApiCreateFolder').and.returnValue(fakeResolvePromise);
|
spyOn(component, 'getFolderNode').and.returnValue(Observable.of(fakeFolderNodeWithPermission));
|
||||||
|
|
||||||
component.ngOnChanges({ rootFolderId: new SimpleChange(null, component.rootFolderId, true) });
|
component.ngOnChanges({ rootFolderId: new SimpleChange(null, component.rootFolderId, true) });
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
|
||||||
component.onSuccess.subscribe(e => {
|
component.onSuccess.subscribe(e => {
|
||||||
expect(e.value).toEqual('File uploaded');
|
expect(e.value).toEqual('File uploaded');
|
||||||
done();
|
done();
|
||||||
@@ -245,22 +222,21 @@ describe('UploadButtonComponent', () => {
|
|||||||
|
|
||||||
spyOn(component, 'uploadFiles').and.callFake(() => {
|
spyOn(component, 'uploadFiles').and.callFake(() => {
|
||||||
component.onSuccess.emit({
|
component.onSuccess.emit({
|
||||||
value: 'File uploaded'
|
value: 'File uploaded'
|
||||||
}
|
});
|
||||||
);
|
|
||||||
});
|
});
|
||||||
component.onDirectoryAdded(fakeEvent);
|
component.onDirectoryAdded(fakeEvent);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should emit an onError event when the folder already exist', (done) => {
|
it('should emit an onError event when the folder already exist', (done) => {
|
||||||
component.rootFolderId = '-my-';
|
component.rootFolderId = '-my-';
|
||||||
spyOn(uploadService, 'getFolderNode').and.returnValue(Observable.of(fakeFolderNodeWithPermission));
|
spyOn(contentService, 'createFolder').and.returnValue(Observable.throw(new Error('')));
|
||||||
spyOn(uploadService, 'callApiCreateFolder').and.returnValue(fakeRejectPromise);
|
spyOn(component, 'getFolderNode').and.returnValue(Observable.of(fakeFolderNodeWithPermission));
|
||||||
|
|
||||||
component.ngOnChanges({ rootFolderId: new SimpleChange(null, component.rootFolderId, true) });
|
component.ngOnChanges({ rootFolderId: new SimpleChange(null, component.rootFolderId, true) });
|
||||||
|
|
||||||
component.onError.subscribe(e => {
|
component.onError.subscribe(e => {
|
||||||
expect(e.value).toEqual('FILE_UPLOAD.MESSAGES.FOLDER_ALREADY_EXIST');
|
expect(e.value).toEqual('Error');
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@@ -16,36 +16,15 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { Component, ElementRef, Input, Output, EventEmitter, OnInit, OnChanges, SimpleChanges } from '@angular/core';
|
import { Component, ElementRef, Input, Output, EventEmitter, OnInit, OnChanges, SimpleChanges } from '@angular/core';
|
||||||
import { Subject } from 'rxjs/Rx';
|
import { Observable, Subject } from 'rxjs/Rx';
|
||||||
import { AlfrescoTranslationService, LogService, NotificationService, AlfrescoSettingsService } from 'ng2-alfresco-core';
|
import { AlfrescoApiService, AlfrescoContentService, AlfrescoTranslationService, LogService, NotificationService, AlfrescoSettingsService } from 'ng2-alfresco-core';
|
||||||
|
import { MinimalNodeEntryEntity } from 'alfresco-js-api';
|
||||||
import { UploadService } from '../services/upload.service';
|
import { UploadService } from '../services/upload.service';
|
||||||
import { FileModel } from '../models/file.model';
|
import { FileModel } from '../models/file.model';
|
||||||
import { PermissionModel } from '../models/permissions.model';
|
import { PermissionModel } from '../models/permissions.model';
|
||||||
|
|
||||||
declare let componentHandler: any;
|
|
||||||
|
|
||||||
const ERROR_FOLDER_ALREADY_EXIST = 409;
|
const ERROR_FOLDER_ALREADY_EXIST = 409;
|
||||||
|
|
||||||
/**
|
|
||||||
* <alfresco-upload-button [showNotificationBar]="boolean"
|
|
||||||
* [uploadFolders]="boolean"
|
|
||||||
* [multipleFiles]="boolean"
|
|
||||||
* [acceptedFilesType]="string"
|
|
||||||
* (onSuccess)="customMethod($event)">
|
|
||||||
* </alfresco-upload-button>
|
|
||||||
*
|
|
||||||
* This component, provide a set of buttons to upload files to alfresco.
|
|
||||||
*
|
|
||||||
* @InputParam {boolean} [true] showNotificationBar - hide/show notification bar.
|
|
||||||
* @InputParam {boolean} [false] versioning - true to indicate that a major version should be created
|
|
||||||
* @InputParam {boolean} [false] uploadFolders - allow/disallow upload folders (only for chrome).
|
|
||||||
* @InputParam {boolean} [false] multipleFiles - allow/disallow multiple files.
|
|
||||||
* @InputParam {string} [*] acceptedFilesType - array of allowed file extensions.
|
|
||||||
* @InputParam {boolean} [false] versioning - true to indicate that a major version should be created
|
|
||||||
* @Output - onSuccess - The event is emitted when the file is uploaded
|
|
||||||
*
|
|
||||||
* @returns {UploadButtonComponent} .
|
|
||||||
*/
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'alfresco-upload-button',
|
selector: 'alfresco-upload-button',
|
||||||
templateUrl: './upload-button.component.html',
|
templateUrl: './upload-button.component.html',
|
||||||
@@ -106,7 +85,9 @@ export class UploadButtonComponent implements OnInit, OnChanges {
|
|||||||
private translateService: AlfrescoTranslationService,
|
private translateService: AlfrescoTranslationService,
|
||||||
private logService: LogService,
|
private logService: LogService,
|
||||||
private notificationService: NotificationService,
|
private notificationService: NotificationService,
|
||||||
private settingsService: AlfrescoSettingsService) {
|
private settingsService: AlfrescoSettingsService,
|
||||||
|
private apiService: AlfrescoApiService,
|
||||||
|
private contentService: AlfrescoContentService) {
|
||||||
if (translateService) {
|
if (translateService) {
|
||||||
translateService.addTranslationFolder('ng2-alfresco-upload', 'assets/ng2-alfresco-upload');
|
translateService.addTranslationFolder('ng2-alfresco-upload', 'assets/ng2-alfresco-upload');
|
||||||
}
|
}
|
||||||
@@ -172,9 +153,9 @@ export class UploadButtonComponent implements OnInit, OnChanges {
|
|||||||
let directoryName = this.getDirectoryName(directoryPath);
|
let directoryName = this.getDirectoryName(directoryPath);
|
||||||
let absolutePath = this.currentFolderPath + this.getDirectoryPath(directoryPath);
|
let absolutePath = this.currentFolderPath + this.getDirectoryPath(directoryPath);
|
||||||
|
|
||||||
this.uploadService.createFolder(absolutePath, directoryName, this.rootFolderId)
|
this.contentService.createFolder(absolutePath, directoryName, this.rootFolderId)
|
||||||
.subscribe(
|
.subscribe(
|
||||||
res => {
|
_ => {
|
||||||
let relativeDir = this.currentFolderPath + '/' + directoryPath;
|
let relativeDir = this.currentFolderPath + '/' + directoryPath;
|
||||||
this.uploadFiles(relativeDir, filesDir);
|
this.uploadFiles(relativeDir, filesDir);
|
||||||
},
|
},
|
||||||
@@ -269,10 +250,8 @@ export class UploadButtonComponent implements OnInit, OnChanges {
|
|||||||
messageTranslate = this.translateService.get('FILE_UPLOAD.MESSAGES.PROGRESS');
|
messageTranslate = this.translateService.get('FILE_UPLOAD.MESSAGES.PROGRESS');
|
||||||
actionTranslate = this.translateService.get('FILE_UPLOAD.ACTION.UNDO');
|
actionTranslate = this.translateService.get('FILE_UPLOAD.ACTION.UNDO');
|
||||||
|
|
||||||
this.notificationService.openSnackMessageAction(messageTranslate.value, actionTranslate.value, 3000).afterDismissed().subscribe(() => {
|
this.notificationService.openSnackMessageAction(messageTranslate.value, actionTranslate.value, 3000).onAction().subscribe(() => {
|
||||||
latestFilesAdded.forEach((uploadingFileModel: FileModel) => {
|
this.uploadService.cancelUpload(...latestFilesAdded);
|
||||||
uploadingFileModel.emitAbort();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -282,11 +261,12 @@ export class UploadButtonComponent implements OnInit, OnChanges {
|
|||||||
* @returns {string}
|
* @returns {string}
|
||||||
*/
|
*/
|
||||||
private getErrorMessage(response: any): string {
|
private getErrorMessage(response: any): string {
|
||||||
if (response.body && response.body.error.statusCode === ERROR_FOLDER_ALREADY_EXIST) {
|
if (response && response.body && response.body.error.statusCode === ERROR_FOLDER_ALREADY_EXIST) {
|
||||||
let errorMessage: any;
|
let errorMessage: any;
|
||||||
errorMessage = this.translateService.get('FILE_UPLOAD.MESSAGES.FOLDER_ALREADY_EXIST');
|
errorMessage = this.translateService.get('FILE_UPLOAD.MESSAGES.FOLDER_ALREADY_EXIST');
|
||||||
return errorMessage.value;
|
return errorMessage.value;
|
||||||
}
|
}
|
||||||
|
return 'Error';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -314,17 +294,30 @@ export class UploadButtonComponent implements OnInit, OnChanges {
|
|||||||
|
|
||||||
checkPermission() {
|
checkPermission() {
|
||||||
if (this.rootFolderId) {
|
if (this.rootFolderId) {
|
||||||
this.uploadService.getFolderNode(this.rootFolderId).subscribe(
|
this.getFolderNode(this.rootFolderId).subscribe(
|
||||||
(res) => {
|
res => this.permissionValue.next(this.hasCreatePermission(res)),
|
||||||
this.permissionValue.next(this.hasCreatePermission(res));
|
error => this.onError.emit(error)
|
||||||
},
|
|
||||||
(error) => {
|
|
||||||
this.onError.emit(error);
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getFolderNode(nodeId: string): Observable<MinimalNodeEntryEntity> {
|
||||||
|
let opts: any = {
|
||||||
|
includeSource: true,
|
||||||
|
include: ['allowableOperations']
|
||||||
|
};
|
||||||
|
|
||||||
|
return Observable.fromPromise(this.apiService.getInstance().nodes.getNodeInfo(nodeId, opts))
|
||||||
|
.catch(err => this.handleError(err));
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
this.logService.error(error);
|
||||||
|
return Observable.throw(error || 'Server error');
|
||||||
|
}
|
||||||
|
|
||||||
private hasCreatePermission(node: any): boolean {
|
private hasCreatePermission(node: any): boolean {
|
||||||
if (this.hasPermissions(node)) {
|
if (this.hasPermissions(node)) {
|
||||||
return node.allowableOperations.find(permision => permision === 'create') ? true : false;
|
return node.allowableOperations.find(permision => permision === 'create') ? true : false;
|
||||||
|
@@ -17,7 +17,7 @@
|
|||||||
|
|
||||||
import { ComponentFixture, TestBed, async } from '@angular/core/testing';
|
import { ComponentFixture, TestBed, async } from '@angular/core/testing';
|
||||||
import { EventEmitter, DebugElement } from '@angular/core';
|
import { EventEmitter, DebugElement } from '@angular/core';
|
||||||
import { AlfrescoTranslationService, CoreModule, LogService, LogServiceMock, NotificationService } from 'ng2-alfresco-core';
|
import { AlfrescoTranslationService, CoreModule, LogService, LogServiceMock } from 'ng2-alfresco-core';
|
||||||
|
|
||||||
import { UploadDragAreaComponent } from './upload-drag-area.component';
|
import { UploadDragAreaComponent } from './upload-drag-area.component';
|
||||||
import { FileDraggableDirective } from '../directives/file-draggable.directive';
|
import { FileDraggableDirective } from '../directives/file-draggable.directive';
|
||||||
@@ -45,7 +45,6 @@ describe('UploadDragAreaComponent', () => {
|
|||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
UploadService,
|
UploadService,
|
||||||
NotificationService,
|
|
||||||
{ provide: AlfrescoTranslationService, useClass: TranslationMock },
|
{ provide: AlfrescoTranslationService, useClass: TranslationMock },
|
||||||
{ provide: LogService, useClass: LogServiceMock }
|
{ provide: LogService, useClass: LogServiceMock }
|
||||||
]
|
]
|
||||||
@@ -147,92 +146,4 @@ describe('UploadDragAreaComponent', () => {
|
|||||||
expect(uploadService.uploadFilesInTheQueue)
|
expect(uploadService.uploadFilesInTheQueue)
|
||||||
.toHaveBeenCalledWith('-my-', '/root-fake-/sites-fake/document-library-fake/folder-fake/', null);
|
.toHaveBeenCalledWith('-my-', '/root-fake-/sites-fake/document-library-fake/folder-fake/', null);
|
||||||
});
|
});
|
||||||
|
|
||||||
xit('should throws an exception and show it in the notification bar when the folder already exist', done => {
|
|
||||||
component.currentFolderPath = '/root-fake-/sites-fake/folder-fake';
|
|
||||||
component.showNotificationBar = true;
|
|
||||||
|
|
||||||
fixture.detectChanges();
|
|
||||||
let fakeRest = {
|
|
||||||
response: {
|
|
||||||
body: {
|
|
||||||
error: {
|
|
||||||
statusCode: 409
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let fakePromise = new Promise(function (resolve, reject) {
|
|
||||||
reject(fakeRest);
|
|
||||||
});
|
|
||||||
spyOn(uploadService, 'callApiCreateFolder').and.returnValue(fakePromise);
|
|
||||||
spyOn(component, 'showErrorNotificationBar').and.callFake( () => {
|
|
||||||
expect(component.showErrorNotificationBar).toHaveBeenCalledWith('FILE_UPLOAD.MESSAGES.FOLDER_ALREADY_EXIST');
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
|
|
||||||
let folderEntry = {
|
|
||||||
fullPath: '/folder-duplicate-fake',
|
|
||||||
isDirectory: true,
|
|
||||||
isFile: false,
|
|
||||||
name: 'folder-duplicate-fake'
|
|
||||||
};
|
|
||||||
|
|
||||||
component.onFolderEntityDropped(folderEntry);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should create a folder and call onFilesEntityDropped with the file inside the folder', done => {
|
|
||||||
component.currentFolderPath = '/root-fake-/sites-fake/document-library-fake';
|
|
||||||
component.onSuccess = new EventEmitter();
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let fakeRest = {
|
|
||||||
entry: {
|
|
||||||
isFile: false,
|
|
||||||
isFolder: true,
|
|
||||||
name: 'folder-fake'
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let fakePromise = new Promise(function (resolve, reject) {
|
|
||||||
resolve(fakeRest);
|
|
||||||
});
|
|
||||||
spyOn(uploadService, 'callApiCreateFolder').and.returnValue(fakePromise);
|
|
||||||
spyOn(component, 'onFilesEntityDropped').and.callFake( () => {
|
|
||||||
expect(component.onFilesEntityDropped).toHaveBeenCalledWith(itemEntity);
|
|
||||||
});
|
|
||||||
|
|
||||||
spyOn(component, 'showUndoNotificationBar').and.callFake( () => {
|
|
||||||
expect(component.showUndoNotificationBar).toHaveBeenCalled();
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
|
|
||||||
let folderEntry = {
|
|
||||||
fullPath: '/folder-fake',
|
|
||||||
isDirectory: true,
|
|
||||||
isFile: false,
|
|
||||||
name: 'folder-fake',
|
|
||||||
createReader: () => {
|
|
||||||
return {
|
|
||||||
readEntries: (callback) => {
|
|
||||||
let entries = [itemEntity, itemEntity];
|
|
||||||
callback(entries);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
component.onFolderEntityDropped(folderEntry);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
@@ -16,21 +16,12 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { Component, Input, Output, EventEmitter } from '@angular/core';
|
import { Component, Input, Output, EventEmitter } from '@angular/core';
|
||||||
import { AlfrescoTranslationService, LogService, NotificationService } from 'ng2-alfresco-core';
|
import { AlfrescoTranslationService, AlfrescoContentService, LogService, NotificationService } from 'ng2-alfresco-core';
|
||||||
import { UploadService } from '../services/upload.service';
|
import { UploadService } from '../services/upload.service';
|
||||||
import { FileModel } from '../models/file.model';
|
import { FileModel } from '../models/file.model';
|
||||||
|
|
||||||
const ERROR_FOLDER_ALREADY_EXIST = 409;
|
const ERROR_FOLDER_ALREADY_EXIST = 409;
|
||||||
|
|
||||||
/**
|
|
||||||
* <alfresco-upload-drag-area (onSuccess)="customMethod($event)></alfresco-upload-drag-area>
|
|
||||||
*
|
|
||||||
* This component, provide a drag and drop are to upload files to alfresco.
|
|
||||||
*
|
|
||||||
* @Output - onSuccess - The event is emitted when the file is uploaded
|
|
||||||
*
|
|
||||||
* @returns {UploadDragAreaComponent} .
|
|
||||||
*/
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'alfresco-upload-drag-area',
|
selector: 'alfresco-upload-drag-area',
|
||||||
templateUrl: './upload-drag-area.component.html',
|
templateUrl: './upload-drag-area.component.html',
|
||||||
@@ -61,7 +52,8 @@ export class UploadDragAreaComponent {
|
|||||||
constructor(private uploadService: UploadService,
|
constructor(private uploadService: UploadService,
|
||||||
private translateService: AlfrescoTranslationService,
|
private translateService: AlfrescoTranslationService,
|
||||||
private logService: LogService,
|
private logService: LogService,
|
||||||
private notificationService: NotificationService) {
|
private notificationService: NotificationService,
|
||||||
|
private contentService: AlfrescoContentService) {
|
||||||
if (translateService) {
|
if (translateService) {
|
||||||
translateService.addTranslationFolder('ng2-alfresco-upload', 'assets/ng2-alfresco-upload');
|
translateService.addTranslationFolder('ng2-alfresco-upload', 'assets/ng2-alfresco-upload');
|
||||||
}
|
}
|
||||||
@@ -129,7 +121,7 @@ export class UploadDragAreaComponent {
|
|||||||
let relativePath = folder.fullPath.replace(folder.name, '');
|
let relativePath = folder.fullPath.replace(folder.name, '');
|
||||||
relativePath = this.currentFolderPath + relativePath;
|
relativePath = this.currentFolderPath + relativePath;
|
||||||
|
|
||||||
this.uploadService.createFolder(relativePath, folder.name, this.rootFolderId)
|
this.contentService.createFolder(relativePath, folder.name, this.rootFolderId)
|
||||||
.subscribe(
|
.subscribe(
|
||||||
message => {
|
message => {
|
||||||
this.onSuccess.emit({
|
this.onSuccess.emit({
|
||||||
@@ -186,9 +178,7 @@ export class UploadDragAreaComponent {
|
|||||||
actionTranslate = this.translateService.get('FILE_UPLOAD.ACTION.UNDO');
|
actionTranslate = this.translateService.get('FILE_UPLOAD.ACTION.UNDO');
|
||||||
|
|
||||||
this.notificationService.openSnackMessageAction(messageTranslate.value, actionTranslate.value, 3000).onAction().subscribe(() => {
|
this.notificationService.openSnackMessageAction(messageTranslate.value, actionTranslate.value, 3000).onAction().subscribe(() => {
|
||||||
latestFilesAdded.forEach((uploadingFileModel: FileModel) => {
|
this.uploadService.cancelUpload(...latestFilesAdded);
|
||||||
uploadingFileModel.emitAbort();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -17,17 +17,6 @@
|
|||||||
|
|
||||||
import { Directive, EventEmitter, Input, Output, OnInit, OnDestroy, ElementRef, NgZone } from '@angular/core';
|
import { Directive, EventEmitter, Input, Output, OnInit, OnDestroy, ElementRef, NgZone } from '@angular/core';
|
||||||
|
|
||||||
/**
|
|
||||||
* [file-draggable]
|
|
||||||
*
|
|
||||||
* This directive, provide a drag and drop area for files and folders.
|
|
||||||
*
|
|
||||||
* @OutputEvent {EventEmitter} onFilesDropped(File)- event fired fot each file dropped
|
|
||||||
* in the drag and drop area.
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* @returns {FileDraggableDirective} .
|
|
||||||
*/
|
|
||||||
@Directive({
|
@Directive({
|
||||||
selector: '[file-draggable]'
|
selector: '[file-draggable]'
|
||||||
})
|
})
|
||||||
|
36
ng2-components/ng2-alfresco-upload/src/events/file.event.ts
Normal file
36
ng2-components/ng2-alfresco-upload/src/events/file.event.ts
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
/*!
|
||||||
|
* @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, FileUploadStatus } from '../models/file.model';
|
||||||
|
|
||||||
|
export class FileUploadEvent {
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
public readonly file: FileModel,
|
||||||
|
public readonly status: FileUploadStatus = FileUploadStatus.Pending,
|
||||||
|
public readonly error: any = null) {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export class FileUploadCompleteEvent extends FileUploadEvent {
|
||||||
|
|
||||||
|
constructor(file: FileModel, public totalComplete: number = 0) {
|
||||||
|
super(file, FileUploadStatus.Complete);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -1,25 +1,27 @@
|
|||||||
{
|
{
|
||||||
"FILE_UPLOAD": {
|
"ADF_FILE_UPLOAD": {
|
||||||
"BUTTON": {
|
"FILE_LIST": {
|
||||||
"UPLOAD_FILE": "Upload file",
|
"NAME": "Name",
|
||||||
"UPLOAD_FOLDER": "Upload folder",
|
"PROGRESS": "Progress",
|
||||||
"CANCEL_ALL": "Cancell all"
|
"SIZE": "Size",
|
||||||
|
"ACTION": "Action"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"MESSAGES": {
|
"FILE_UPLOAD": {
|
||||||
"SINGLE_COMPLETED": "upload complete",
|
"BUTTON": {
|
||||||
"COMPLETED": "uploads complete",
|
"UPLOAD_FILE": "Upload file",
|
||||||
"PROGRESS": "Upload in progress...",
|
"UPLOAD_FOLDER": "Upload folder",
|
||||||
"FOLDER_ALREADY_EXIST": "The folder {0} already exist",
|
"CANCEL_ALL": "Cancell all"
|
||||||
"FOLDER_NOT_SUPPORTED": "Folder upload isn't supported by your browser"
|
},
|
||||||
},
|
"MESSAGES": {
|
||||||
"FILE_INFO": {
|
"SINGLE_COMPLETED": "upload complete",
|
||||||
"NAME": "File name",
|
"COMPLETED": "uploads complete",
|
||||||
"PROGRESS": "File progress",
|
"PROGRESS": "Upload in progress...",
|
||||||
"SIZE": "File size",
|
"FOLDER_ALREADY_EXIST": "The folder {0} already exist",
|
||||||
"ACTION": "Actions"
|
"FOLDER_NOT_SUPPORTED": "Folder upload isn't supported by your browser"
|
||||||
},
|
},
|
||||||
"ACTION": {
|
"ACTION": {
|
||||||
"UNDO": "Undo"
|
"UNDO": "Undo"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@@ -15,130 +15,58 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
export interface FileUploadProgress {
|
||||||
*
|
loaded: number;
|
||||||
* This object represent the status of an uploading file.
|
total: number;
|
||||||
*
|
percent: number;
|
||||||
*
|
|
||||||
* @returns {FileModel} .
|
|
||||||
*/
|
|
||||||
export class FileModel {
|
|
||||||
id: string;
|
|
||||||
status: number;
|
|
||||||
statusText: string;
|
|
||||||
progress: Object;
|
|
||||||
name: string;
|
|
||||||
size: string;
|
|
||||||
response: string;
|
|
||||||
done: boolean = false;
|
|
||||||
error: boolean = false;
|
|
||||||
abort: boolean = false;
|
|
||||||
uploading: boolean = false;
|
|
||||||
file: File;
|
|
||||||
promiseUpload: any;
|
|
||||||
|
|
||||||
options: FileUploadOptions;
|
|
||||||
|
|
||||||
constructor(file: File, options?: FileUploadOptions) {
|
|
||||||
this.file = file;
|
|
||||||
this.options = Object.assign({}, {
|
|
||||||
newVersion: false
|
|
||||||
}, options);
|
|
||||||
this.id = this.generateId();
|
|
||||||
this.name = file.name;
|
|
||||||
this.size = this.getFileSize(file.size);
|
|
||||||
this.progress = {
|
|
||||||
loaded: 0,
|
|
||||||
total: 0,
|
|
||||||
percent: 0
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
setProgres(progress: any): void {
|
|
||||||
this.progress = progress;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Emit an event progress on the promise
|
|
||||||
*/
|
|
||||||
emitProgres(progress: any): void {
|
|
||||||
this.setProgres(progress);
|
|
||||||
this.promiseUpload.emit('progress', progress);
|
|
||||||
}
|
|
||||||
|
|
||||||
setError(): void {
|
|
||||||
this.error = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Emit an event progress on the promise
|
|
||||||
*/
|
|
||||||
emitError(): void {
|
|
||||||
this.setError();
|
|
||||||
this.promiseUpload.emit('error');
|
|
||||||
}
|
|
||||||
|
|
||||||
setUploading() {
|
|
||||||
this.uploading = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
setPromiseUpload(promiseUpload: any) {
|
|
||||||
this.promiseUpload = promiseUpload;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Stop the uploading of the file.
|
|
||||||
*/
|
|
||||||
setAbort(): void {
|
|
||||||
if (!this.done && !this.error) {
|
|
||||||
this.abort = true;
|
|
||||||
this.uploading = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Emit an event abort on the promise
|
|
||||||
*/
|
|
||||||
emitAbort(): void {
|
|
||||||
this.setAbort();
|
|
||||||
this.promiseUpload.abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update status of the file when upload finish or is ended.
|
|
||||||
*/
|
|
||||||
onFinished(status: number, statusText: string, response: string): void {
|
|
||||||
this.status = status;
|
|
||||||
this.statusText = statusText;
|
|
||||||
this.response = response;
|
|
||||||
this.done = true;
|
|
||||||
this.uploading = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Calculate the size of the file in kb,mb and gb.
|
|
||||||
*
|
|
||||||
* @param {number} sizeinbytes - size in bytes of the file.
|
|
||||||
*/
|
|
||||||
private getFileSize(sizeinbytes: number): string {
|
|
||||||
let fSExt = new Array('Bytes', 'KB', 'MB', 'GB');
|
|
||||||
let size = sizeinbytes;
|
|
||||||
let i = 0;
|
|
||||||
while (size > 900) {
|
|
||||||
size /= 1000;
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
return Math.round((Math.round(size * 100) / 100)) + ' ' + fSExt[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
private generateId(): string {
|
|
||||||
return 'uploading-file-' + 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
|
|
||||||
let r = Math.random() * 16 | 0, v = c === 'x' ? r : (r & 0x3 | 0x8);
|
|
||||||
return v.toString(16);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface FileUploadOptions {
|
export interface FileUploadOptions {
|
||||||
newVersion?: boolean;
|
newVersion?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum FileUploadStatus {
|
||||||
|
Pending = 0,
|
||||||
|
Complete = 1,
|
||||||
|
Starting = 2,
|
||||||
|
Progress = 3,
|
||||||
|
Cancelled = 4,
|
||||||
|
Aborted = 5,
|
||||||
|
Error = 6
|
||||||
|
}
|
||||||
|
|
||||||
|
export class FileModel {
|
||||||
|
readonly id: string;
|
||||||
|
readonly name: string;
|
||||||
|
readonly size: number;
|
||||||
|
readonly file: File;
|
||||||
|
|
||||||
|
status: FileUploadStatus = FileUploadStatus.Pending;
|
||||||
|
progress: FileUploadProgress;
|
||||||
|
options: FileUploadOptions;
|
||||||
|
|
||||||
|
constructor(file: File, options?: FileUploadOptions) {
|
||||||
|
this.file = file;
|
||||||
|
|
||||||
|
this.id = this.generateId();
|
||||||
|
this.name = file.name;
|
||||||
|
this.size = file.size;
|
||||||
|
|
||||||
|
this.progress = {
|
||||||
|
loaded: 0,
|
||||||
|
total: 0,
|
||||||
|
percent: 0
|
||||||
|
};
|
||||||
|
|
||||||
|
this.options = Object.assign({}, {
|
||||||
|
newVersion: false
|
||||||
|
}, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
private generateId(): string {
|
||||||
|
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
|
||||||
|
let r = Math.random() * 16 | 0, v = c === 'x' ? r : (r & 0x3 | 0x8);
|
||||||
|
return v.toString(16);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@@ -124,89 +124,7 @@ describe('UploadService', () => {
|
|||||||
service.uploadFilesInTheQueue('-root-', '', emitter);
|
service.uploadFilesInTheQueue('-root-', '', emitter);
|
||||||
|
|
||||||
let file = service.getQueue();
|
let file = service.getQueue();
|
||||||
file[0].emitAbort();
|
service.cancelUpload(...file);
|
||||||
});
|
|
||||||
|
|
||||||
it('should make XHR error request after the xhr error is called', (done) => {
|
|
||||||
let emitter = new EventEmitter();
|
|
||||||
|
|
||||||
emitter.subscribe(e => {
|
|
||||||
expect(e.value).toBe('Error file uploaded');
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
let fileFake = new FileModel(<File>{name: 'fake-name', size: 10});
|
|
||||||
service.addToQueue(fileFake);
|
|
||||||
service.uploadFilesInTheQueue('-root-', '', emitter);
|
|
||||||
|
|
||||||
let file = service.getQueue();
|
|
||||||
file[0].emitError();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should make XHR progress request after the onprogress is called', (done) => {
|
|
||||||
let fakeProgress = {
|
|
||||||
loaded: 500,
|
|
||||||
total: 1234,
|
|
||||||
percent: 44
|
|
||||||
};
|
|
||||||
let filesFake = new FileModel(<File>{name: 'fake-name', size: 10});
|
|
||||||
service.addToQueue(filesFake);
|
|
||||||
service.filesUpload$.subscribe((file) => {
|
|
||||||
expect(file).toBeDefined();
|
|
||||||
expect(file[0]).toBeDefined();
|
|
||||||
expect(file[0].progress).toEqual(fakeProgress);
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
service.uploadFilesInTheQueue('-root-', '', null);
|
|
||||||
|
|
||||||
let file = service.getQueue();
|
|
||||||
|
|
||||||
file[0].emitProgres(fakeProgress);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should make XHR done request after the folder is created', (done) => {
|
|
||||||
let fakeRest = {
|
|
||||||
entry: {
|
|
||||||
isFile: false,
|
|
||||||
isFolder: true,
|
|
||||||
name: 'fake-folder'
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let fakePromise = new Promise(function (resolve, reject) {
|
|
||||||
resolve(fakeRest);
|
|
||||||
});
|
|
||||||
spyOn(service, 'callApiCreateFolder').and.returnValue(fakePromise);
|
|
||||||
let defaultPath = '';
|
|
||||||
let folderName = 'fake-folder';
|
|
||||||
service.createFolder(defaultPath, folderName).subscribe(res => {
|
|
||||||
expect(res).toEqual(fakeRest);
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should throws an exception when a folder already exist', (done) => {
|
|
||||||
let fakeRest = {
|
|
||||||
response: {
|
|
||||||
body: {
|
|
||||||
error: {
|
|
||||||
statusCode: 409
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let fakePromise = new Promise(function (resolve, reject) {
|
|
||||||
reject(fakeRest);
|
|
||||||
});
|
|
||||||
spyOn(service, 'callApiCreateFolder').and.returnValue(fakePromise);
|
|
||||||
let defaultPath = '';
|
|
||||||
let folderName = 'folder-duplicate-fake';
|
|
||||||
service.createFolder(defaultPath, folderName).subscribe(
|
|
||||||
res => {
|
|
||||||
},
|
|
||||||
error => {
|
|
||||||
expect(error).toEqual(fakeRest);
|
|
||||||
done();
|
|
||||||
}
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('If versioning is true autoRename should not be present and majorVersion should be a param', () => {
|
it('If versioning is true autoRename should not be present and majorVersion should be a param', () => {
|
||||||
|
@@ -16,36 +16,38 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { EventEmitter, Injectable } from '@angular/core';
|
import { EventEmitter, Injectable } from '@angular/core';
|
||||||
import { Response } from '@angular/http';
|
import { Subject } from 'rxjs/Rx';
|
||||||
import { Observer, Observable, Subject } from 'rxjs/Rx';
|
|
||||||
import { AlfrescoApiService, LogService } from 'ng2-alfresco-core';
|
import { AlfrescoApiService, LogService } from 'ng2-alfresco-core';
|
||||||
import { FolderCreatedEvent } from '../events/folder-created.event';
|
import { FileUploadEvent, FileUploadCompleteEvent } from '../events/file.event';
|
||||||
import { FileModel } from '../models/file.model';
|
import { FileModel, FileUploadProgress, FileUploadStatus } from '../models/file.model';
|
||||||
import { MinimalNodeEntity, MinimalNodeEntryEntity } from 'alfresco-js-api';
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* UploadService keep the queue of the file to upload and uploads them.
|
|
||||||
*
|
|
||||||
* @returns {UploadService} .
|
|
||||||
*/
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class UploadService {
|
export class UploadService {
|
||||||
|
|
||||||
private queue: FileModel[] = [];
|
private queue: FileModel[] = [];
|
||||||
private filesUploadObserverProgressBar: Observer<FileModel[]>;
|
private cache: { [key: string]: any } = {};
|
||||||
private totalCompletedObserver: Observer<number>;
|
private totalComplete: number = 0;
|
||||||
|
|
||||||
totalCompleted: number = 0;
|
queueChanged: Subject<FileModel[]> = new Subject<FileModel[]>();
|
||||||
filesUpload$: Observable<FileModel[]>;
|
fileUpload: Subject<FileUploadEvent> = new Subject<FileUploadEvent>();
|
||||||
totalCompleted$: Observable<any>;
|
fileUploadStarting: Subject<FileUploadEvent> = new Subject<FileUploadEvent>();
|
||||||
|
fileUploadCancelled: Subject<FileUploadEvent> = new Subject<FileUploadEvent>();
|
||||||
folderCreated: Subject<FolderCreatedEvent> = new Subject<FolderCreatedEvent>();
|
fileUploadProgress: Subject<FileUploadEvent> = new Subject<FileUploadEvent>();
|
||||||
|
fileUploadAborted: Subject<FileUploadEvent> = new Subject<FileUploadEvent>();
|
||||||
|
fileUploadError: Subject<FileUploadEvent> = new Subject<FileUploadEvent>();
|
||||||
|
fileUploadComplete: Subject<FileUploadCompleteEvent> = new Subject<FileUploadCompleteEvent>();
|
||||||
|
|
||||||
constructor(private apiService: AlfrescoApiService,
|
constructor(private apiService: AlfrescoApiService,
|
||||||
private logService: LogService) {
|
private logService: LogService) {
|
||||||
this.filesUpload$ = new Observable<FileModel[]>(observer => this.filesUploadObserverProgressBar = observer).share();
|
}
|
||||||
this.totalCompleted$ = new Observable<number>(observer => this.totalCompletedObserver = observer).share();
|
|
||||||
|
/**
|
||||||
|
* Returns the file Queue
|
||||||
|
*
|
||||||
|
* @return {FileModel[]} - files in the upload queue.
|
||||||
|
*/
|
||||||
|
getQueue(): FileModel[] {
|
||||||
|
return this.queue;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -59,9 +61,7 @@ export class UploadService {
|
|||||||
addToQueue(...files: FileModel[]): FileModel[] {
|
addToQueue(...files: FileModel[]): FileModel[] {
|
||||||
const allowedFiles = files.filter(f => !f.name.startsWith('.'));
|
const allowedFiles = files.filter(f => !f.name.startsWith('.'));
|
||||||
this.queue = this.queue.concat(allowedFiles);
|
this.queue = this.queue.concat(allowedFiles);
|
||||||
if (this.filesUploadObserverProgressBar) {
|
this.queueChanged.next(this.queue);
|
||||||
this.filesUploadObserverProgressBar.next(this.queue);
|
|
||||||
}
|
|
||||||
return allowedFiles;
|
return allowedFiles;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -69,125 +69,142 @@ export class UploadService {
|
|||||||
* Pick all the files in the queue that are not been uploaded yet and upload it into the directory folder.
|
* Pick all the files in the queue that are not been uploaded yet and upload it into the directory folder.
|
||||||
*/
|
*/
|
||||||
uploadFilesInTheQueue(rootId: string, directory: string, elementEmit: EventEmitter<any>): void {
|
uploadFilesInTheQueue(rootId: string, directory: string, elementEmit: EventEmitter<any>): void {
|
||||||
let filesToUpload = this.queue.filter((file) => {
|
const files = this.getFilesToUpload();
|
||||||
return !file.uploading && !file.done && !file.abort && !file.error;
|
|
||||||
});
|
|
||||||
|
|
||||||
filesToUpload.forEach((uploadingFileModel: FileModel) => {
|
files.forEach((file: FileModel) => {
|
||||||
uploadingFileModel.setUploading();
|
this.onUploadStarting(file);
|
||||||
|
|
||||||
const opts: any = {
|
const opts: any = {
|
||||||
renditions: 'doclib'
|
renditions: 'doclib'
|
||||||
};
|
};
|
||||||
|
|
||||||
if (uploadingFileModel.options.newVersion === true) {
|
if (file.options.newVersion === true) {
|
||||||
opts.overwrite = true;
|
opts.overwrite = true;
|
||||||
opts.majorVersion = true;
|
opts.majorVersion = true;
|
||||||
} else {
|
} else {
|
||||||
opts.autoRename = true;
|
opts.autoRename = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
let promiseUpload = this.apiService.getInstance().upload.uploadFile(uploadingFileModel.file, directory, rootId, null, opts)
|
const promise = this.apiService.getInstance().upload.uploadFile(file.file, directory, rootId, null, opts);
|
||||||
.on('progress', (progress: any) => {
|
promise.on('progress', (progress: FileUploadProgress) => {
|
||||||
uploadingFileModel.setProgres(progress);
|
this.onUploadProgress(file, progress);
|
||||||
this.updateFileListStream(this.queue);
|
})
|
||||||
})
|
.on('abort', () => {
|
||||||
.on('abort', () => {
|
this.onUploadAborted(file);
|
||||||
uploadingFileModel.setAbort();
|
elementEmit.emit({
|
||||||
elementEmit.emit({
|
value: 'File aborted'
|
||||||
value: 'File aborted'
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.on('error', () => {
|
|
||||||
uploadingFileModel.setError();
|
|
||||||
elementEmit.emit({
|
|
||||||
value: 'Error file uploaded'
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.on('success', (data: any) => {
|
|
||||||
elementEmit.emit({
|
|
||||||
value: data
|
|
||||||
});
|
|
||||||
uploadingFileModel.onFinished(
|
|
||||||
data.status,
|
|
||||||
data.statusText,
|
|
||||||
data.response
|
|
||||||
);
|
|
||||||
|
|
||||||
this.updateFileListStream(this.queue);
|
|
||||||
if (!uploadingFileModel.abort && !uploadingFileModel.error) {
|
|
||||||
this.updateFileCounterStream(++this.totalCompleted);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
})
|
||||||
|
.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);
|
||||||
|
});
|
||||||
|
|
||||||
uploadingFileModel.setPromiseUpload(promiseUpload);
|
this.cache[file.id] = promise;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
cancelUpload(...files: FileModel[]) {
|
||||||
* Return all the files in the uploading queue.
|
files.forEach(file => {
|
||||||
*
|
file.status = FileUploadStatus.Cancelled;
|
||||||
* @return {FileModel[]} - files in the upload queue.
|
|
||||||
*/
|
const promise = this.cache[file.id];
|
||||||
getQueue(): FileModel[] {
|
if (promise) {
|
||||||
return this.queue;
|
promise.abort();
|
||||||
|
delete this.cache[file.id];
|
||||||
|
}
|
||||||
|
|
||||||
|
const event = new FileUploadEvent(file, FileUploadStatus.Cancelled);
|
||||||
|
this.fileUpload.next(event);
|
||||||
|
this.fileUploadCancelled.next(event);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private onUploadStarting(file: FileModel): void {
|
||||||
* Create a folder
|
if (file) {
|
||||||
* @param name - the folder name
|
file.status = FileUploadStatus.Starting;
|
||||||
*/
|
const event = new FileUploadEvent(file, FileUploadStatus.Starting);
|
||||||
createFolder(relativePath: string, name: string, parentId?: string): Observable<MinimalNodeEntity> {
|
this.fileUpload.next(event);
|
||||||
return Observable.fromPromise(this.callApiCreateFolder(relativePath, name, parentId))
|
this.fileUploadStarting.next(event);
|
||||||
.do(data => {
|
|
||||||
this.folderCreated.next({
|
|
||||||
relativePath: relativePath,
|
|
||||||
name: name,
|
|
||||||
parentId: parentId,
|
|
||||||
node: data
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.catch(err => this.handleError(err));
|
|
||||||
}
|
|
||||||
|
|
||||||
callApiCreateFolder(relativePath: string, name: string, parentId?: string): Promise<MinimalNodeEntity> {
|
|
||||||
return this.apiService.getInstance().nodes.createFolder(name, relativePath, parentId);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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
|
|
||||||
this.logService.error(error);
|
|
||||||
return Observable.throw(error || 'Server error');
|
|
||||||
}
|
|
||||||
|
|
||||||
private updateFileListStream(fileList: FileModel[]) {
|
|
||||||
if (this.filesUploadObserverProgressBar) {
|
|
||||||
this.filesUploadObserverProgressBar.next(fileList);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
updateFileCounterStream(total: number) {
|
private onUploadProgress(file: FileModel, progress: FileUploadProgress): void {
|
||||||
if (this.totalCompletedObserver) {
|
if (file) {
|
||||||
this.totalCompletedObserver.next(total);
|
file.progress = progress;
|
||||||
|
file.status = FileUploadStatus.Progress;
|
||||||
|
|
||||||
|
const event = new FileUploadEvent(file, FileUploadStatus.Progress);
|
||||||
|
this.fileUpload.next(event);
|
||||||
|
this.fileUploadProgress.next(event);
|
||||||
|
|
||||||
|
this.queueChanged.next(this.queue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getFolderNode(nodeId: string): Observable<MinimalNodeEntryEntity> {
|
private onUploadError(file: FileModel, error: any): void {
|
||||||
let opts: any = {
|
if (file) {
|
||||||
includeSource: true,
|
file.status = FileUploadStatus.Error;
|
||||||
include: ['allowableOperations']
|
|
||||||
};
|
|
||||||
|
|
||||||
return Observable.fromPromise(this.apiService.getInstance().nodes.getNodeInfo(nodeId, opts))
|
const promise = this.cache[file.id];
|
||||||
.map((response: any) => {
|
if (promise) {
|
||||||
return response;
|
delete this.cache[file.id];
|
||||||
})
|
}
|
||||||
.catch(err => this.handleError(err));
|
|
||||||
|
const event = new FileUploadEvent(file, FileUploadStatus.Error, error);
|
||||||
|
this.fileUpload.next(event);
|
||||||
|
this.fileUploadError.next(event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private onUploadComplete(file: FileModel): void {
|
||||||
|
if (file) {
|
||||||
|
file.status = FileUploadStatus.Complete;
|
||||||
|
this.totalComplete++;
|
||||||
|
|
||||||
|
const promise = this.cache[file.id];
|
||||||
|
if (promise) {
|
||||||
|
delete this.cache[file.id];
|
||||||
|
}
|
||||||
|
|
||||||
|
const event = new FileUploadCompleteEvent(file, this.totalComplete);
|
||||||
|
this.fileUpload.next(event);
|
||||||
|
this.fileUploadComplete.next(event);
|
||||||
|
|
||||||
|
this.queueChanged.next(this.queue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private onUploadAborted(file: FileModel): void {
|
||||||
|
if (file) {
|
||||||
|
file.status = FileUploadStatus.Aborted;
|
||||||
|
|
||||||
|
const promise = this.cache[file.id];
|
||||||
|
if (promise) {
|
||||||
|
delete this.cache[file.id];
|
||||||
|
}
|
||||||
|
|
||||||
|
const event = new FileUploadEvent(file, FileUploadStatus.Aborted);
|
||||||
|
this.fileUpload.next(event);
|
||||||
|
this.fileUploadAborted.next(event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private getFilesToUpload(): FileModel[] {
|
||||||
|
let filesToUpload = this.queue.filter(file => {
|
||||||
|
return file.status === FileUploadStatus.Pending;
|
||||||
|
});
|
||||||
|
return filesToUpload;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user