[ADF-712] Task Attachment - Provide a way to attach a new content (#1898)

* create button, download, view functionality added in task attachment list component

* created sevice to attach document to task

* added new component to create/uplaod attachment to task

* added new component to create/uplaod attachment to task

* added test case for create task attachment component

* added test case for create task attachment component

* added input to block upload document  to ECM

* fixed create task attachment spec file issue

* changed alfresco-upload to alfresco-core upload directive

* removed attachCreate button and emitter from task-attachment-list

* removed uploadToEcm input and checkValidity method from alfresco-upload

* added documentation for task-attachment-list and create-task-attachment components
This commit is contained in:
Infad Kachancheri
2017-06-01 20:55:50 +05:30
committed by Eugenio Romano
parent c445b6a4df
commit 8afe6e2125
13 changed files with 320 additions and 9 deletions

View File

@@ -123,4 +123,9 @@ export class ActivitiContentService {
let icon = this.mimeTypeIcons[mimeType]; let icon = this.mimeTypeIcons[mimeType];
return icon || ActivitiContentService.DEFAULT_MIME_TYPE_ICON; return icon || ActivitiContentService.DEFAULT_MIME_TYPE_ICON;
} }
createTaskRelatedContent(taskId: string, file: any) {
return Observable.fromPromise(this.apiService.getInstance().activiti.contentApi.createRelatedContentOnTask(taskId, file))
.catch(err => this.handleError(err));
}
} }

View File

@@ -401,6 +401,58 @@ The component shows all the available filters.
<activiti-checklist [readOnly]="false" [taskId]="taskId" [assignee]="taskAssignee.id" #activitichecklist></activiti-checklist> <activiti-checklist [readOnly]="false" [taskId]="taskId" [assignee]="taskAssignee.id" #activitichecklist></activiti-checklist>
``` ```
### Task Attachment List component
This component displays attached documents on a specified task
```html
<adf-task-attachment-list [taskId]="YOUR_TASK_ID"
(attachmentClick)="YOUR_ATTACHMENT_CLICK_EMITTER_HANDLER"></adf-task-attachment-list>
```
![task-attachment-list-sample](docs/assets/task-attachment-list.png)
#### Options
| Name | Description |
| --- | --- |
| `taskId` | (required): The numeric ID of the task to display |
#### Events
| Name | Description |
| --- | --- |
| `attachmentClick` | Emitted when the attachment double clicked or selected view option from context menu by the user from within the component |
### Create Task Attachment component
This component displays Upload Component(Drag and Click) to upload the attachment to a specified task
```html
<activiti-create-task-attachment [taskId]="YOUR_TASK_ID"
(error)="YOUR_CREATE_ATTACHMENT_ERROR_HANDLER"
(success)="YOUR_CREATE_ATTACHMENT_SUCCESS_HANDLER"></activiti-create-task-attachment>
```
![task-create-attachment](docs/assets/task-create-attachment.png)
#### Options
| Name | Description |
| --- | --- |
| `taskId` | (required): The numeric ID of the task to display |
#### Events
| Name | Description |
| --- | --- |
| `error` | Emitted when the error occured while creating/uploading the attachment by the user from within the component |
| `success` | Emitted when the attachement created/uploaded successfully from within the component |
## Build from sources ## Build from sources
Alternatively you can build component from sources with the following commands: Alternatively you can build component from sources with the following commands:

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

View File

@@ -35,7 +35,8 @@ import {
ActivitiTaskHeader, ActivitiTaskHeader,
ActivitiStartTaskButton, ActivitiStartTaskButton,
ActivitiPeopleSearch, ActivitiPeopleSearch,
TaskAttachmentListComponent TaskAttachmentListComponent,
ActivitiCreateTaskAttachmentComponent
} from './src/components/index'; } from './src/components/index';
export * from './src/components/index'; export * from './src/components/index';
@@ -54,7 +55,8 @@ export const ACTIVITI_TASKLIST_DIRECTIVES: any[] = [
ActivitiTaskHeader, ActivitiTaskHeader,
ActivitiStartTaskButton, ActivitiStartTaskButton,
ActivitiPeopleSearch, ActivitiPeopleSearch,
TaskAttachmentListComponent TaskAttachmentListComponent,
ActivitiCreateTaskAttachmentComponent
]; ];
export const ACTIVITI_TASKLIST_PROVIDERS: any[] = [ export const ACTIVITI_TASKLIST_PROVIDERS: any[] = [

View File

@@ -0,0 +1,19 @@
.upload-attachment-container {
border: 1px solid rgb(224, 224, 224);
background: #fff;
text-align: left;
border-top: none;
padding: 10px;
text-align: center;
}
.drag-area {
border: 1px solid #eee;
padding: 100px 10px;
margin-bottom: 10px;
}
.upload-attachment-container button {
color: rgb(253, 145, 0);
opacity: 0.64;
}

View File

@@ -0,0 +1,15 @@
<div class="upload-attachment-container">
<div class="drag-area"
(upload-files)="onFileUpload($event)"
mode="['click', 'drop']"
[adf-upload]="true">
Drop Files Here...
</div>
<button class="mdl-button mdl-js-button mdl-button--raised"
[adf-upload]="true"
mode="['click']"
[multiple]="true"
(upload-files)="onFileUpload($event)">
Upload Attachment
</button>
</div>

View File

@@ -0,0 +1,102 @@
/*!
* @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 { SimpleChange } from '@angular/core';
import { ComponentFixture, TestBed, async } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import { Observable } from 'rxjs/Rx';
import { AlfrescoTranslationService, CoreModule } from 'ng2-alfresco-core';
import { ActivitiContentService } from 'ng2-activiti-form';
import { ActivitiCreateTaskAttachmentComponent } from './activiti-create-task-attachment.component';
describe('Activiti Task Create Attachment', () => {
let componentHandler: any;
let service: ActivitiContentService;
let component: ActivitiCreateTaskAttachmentComponent;
let fixture: ComponentFixture<ActivitiCreateTaskAttachmentComponent>;
let createTaskRelatedContentSpy: jasmine.Spy;
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [
CoreModule.forRoot()
],
declarations: [
ActivitiCreateTaskAttachmentComponent
],
providers: [
{ provide: AlfrescoTranslationService },
ActivitiContentService
]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(ActivitiCreateTaskAttachmentComponent);
component = fixture.componentInstance;
service = fixture.debugElement.injector.get(ActivitiContentService);
createTaskRelatedContentSpy = spyOn(service, 'createTaskRelatedContent').and.returnValue(Observable.of(
{
status: true
}));
componentHandler = jasmine.createSpyObj('componentHandler', [
'upgradeAllRegistered',
'upgradeElement'
]);
window['componentHandler'] = componentHandler;
});
it('should not call createTaskRelatedContent service when taskId changed', () => {
let change = new SimpleChange(null, '123', true);
component.ngOnChanges({ 'taskId': change });
expect(createTaskRelatedContentSpy).not.toHaveBeenCalled();
});
it('should not call createTaskRelatedContent service when there is no file uploaded', () => {
let change = new SimpleChange(null, '123', true);
component.ngOnChanges({ 'taskId': change });
let customEvent = {
detail: {
files: [
]
}
};
component.onFileUpload(customEvent);
expect(createTaskRelatedContentSpy).not.toHaveBeenCalled();
});
it('should call createTaskRelatedContent service when there is a file uploaded', () => {
let change = new SimpleChange(null, '123', true);
component.ngOnChanges({ 'taskId': change });
let file = new File([new Blob()], 'Test');
let customEvent = {
detail: {
files: [
file
]
}
};
component.onFileUpload(customEvent);
expect(createTaskRelatedContentSpy).toHaveBeenCalled();
});
});

View File

@@ -0,0 +1,67 @@
/*!
* @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 { Component, OnChanges, Input, SimpleChanges, Output, EventEmitter } from '@angular/core';
import { AlfrescoTranslationService } from 'ng2-alfresco-core';
import { ActivitiContentService } from 'ng2-activiti-form';
@Component({
selector: 'activiti-create-task-attachment',
styleUrls: ['./activiti-create-task-attachment.component.css'],
templateUrl: './activiti-create-task-attachment.component.html'
})
export class ActivitiCreateTaskAttachmentComponent implements OnChanges {
@Input()
taskId: string;
@Output()
error: EventEmitter<any> = new EventEmitter<any>();
@Output()
success: EventEmitter<any> = new EventEmitter<any>();
constructor(private translateService: AlfrescoTranslationService,
private activitiContentService: ActivitiContentService) {
if (translateService) {
translateService.addTranslationFolder('ng2-activiti-tasklist', 'node_modules/ng2-activiti-tasklist/src');
}
}
ngOnChanges(changes: SimpleChanges) {
if (changes['taskId'] && changes['taskId'].currentValue) {
this.taskId = changes['taskId'].currentValue;
}
}
onFileUpload(event: any) {
let files: File[] = event.detail.files;
for (let i = 0; i < files.length; i++) {
let file: File = files[i];
this.activitiContentService.createTaskRelatedContent(this.taskId, file).subscribe(
(res) => {
this.success.emit(res);
},
(err) => {
this.error.emit(err);
}
);
}
}
}

View File

@@ -6,3 +6,12 @@ alfresco-datatable >>> .column-header {
alfresco-datatable >>> .data-cell { alfresco-datatable >>> .data-cell {
cursor: pointer !important; cursor: pointer !important;
} }
.no-attachment-message {
border: 1px solid rgb(224, 224, 224);
background: #fff;
text-align: left;
border-top: none;
padding: 10px;
text-align: center;
}

View File

@@ -1,3 +1,6 @@
<div class="no-attachment-message" *ngIf="isEmpty()">
No Attachments Found
</div>
<alfresco-datatable *ngIf="!isEmpty()" <alfresco-datatable *ngIf="!isEmpty()"
[rows]="attachments" [rows]="attachments"
[actions]="true" [actions]="true"

View File

@@ -16,7 +16,7 @@
*/ */
import { Component, OnChanges, Input, Output, EventEmitter, SimpleChanges } from '@angular/core'; import { Component, OnChanges, Input, Output, EventEmitter, SimpleChanges } from '@angular/core';
import { AlfrescoTranslationService } from 'ng2-alfresco-core'; import { AlfrescoTranslationService, ContentService } from 'ng2-alfresco-core';
import { ActivitiContentService } from 'ng2-activiti-form'; import { ActivitiContentService } from 'ng2-activiti-form';
@Component({ @Component({
@@ -35,10 +35,14 @@ export class TaskAttachmentListComponent implements OnChanges {
@Output() @Output()
success = new EventEmitter(); success = new EventEmitter();
@Output()
error: EventEmitter<any> = new EventEmitter<any>();
attachments: any[] = []; attachments: any[] = [];
constructor(private translateService: AlfrescoTranslationService, constructor(private translateService: AlfrescoTranslationService,
private activitiContentService: ActivitiContentService) { private activitiContentService: ActivitiContentService,
private contentService: ContentService) {
if (translateService) { if (translateService) {
translateService.addTranslationFolder('ng2-activiti-tasklist', 'node_modules/ng2-activiti-tasklist/src'); translateService.addTranslationFolder('ng2-activiti-tasklist', 'node_modules/ng2-activiti-tasklist/src');
@@ -90,31 +94,63 @@ export class TaskAttachmentListComponent implements OnChanges {
} }
onShowRowActionsMenu(event: any) { onShowRowActionsMenu(event: any) {
let myAction = { let viewAction = {
title: 'Delete', title: 'View',
name: 'delete' name: 'view'
}; };
let removeAction = {
title: 'Remove',
name: 'remove'
};
let downloadAction = {
title: 'Download',
name: 'download'
};
event.value.actions = [ event.value.actions = [
myAction viewAction,
removeAction,
downloadAction
]; ];
} }
onExecuteRowAction(event: any) { onExecuteRowAction(event: any) {
let args = event.value; let args = event.value;
let action = args.action; let action = args.action;
if (action.name === 'delete') { if (action.name === 'view') {
this.emitDocumentContent(args.row.obj);
} else if (action.name === 'remove') {
this.deleteAttachmentById(args.row.obj.id); this.deleteAttachmentById(args.row.obj.id);
} else if (action.name === 'download') {
this.downloadContent(args.row.obj);
} }
} }
openContent(event: any): void { openContent(event: any): void {
let content = event.value.obj; let content = event.value.obj;
this.emitDocumentContent(content);
}
emitDocumentContent(content: any) {
this.activitiContentService.getFileRawContent(content.id).subscribe( this.activitiContentService.getFileRawContent(content.id).subscribe(
(blob: Blob) => { (blob: Blob) => {
content.contentBlob = blob; content.contentBlob = blob;
this.attachmentClick.emit(content); this.attachmentClick.emit(content);
},
(err) => {
this.error.emit(err);
} }
); );
} }
downloadContent(content: any): void {
this.activitiContentService.getFileRawContent(content.id).subscribe(
(blob: Blob) => this.contentService.downloadBlob(blob, content.name),
(err) => {
this.error.emit(err);
}
);
}
} }

View File

@@ -27,3 +27,4 @@ export * from './activiti-filters.component';
export * from './activiti-task-details.component'; export * from './activiti-task-details.component';
export * from './activiti-start-task.component'; export * from './activiti-start-task.component';
export * from './activiti-people-search.component'; export * from './activiti-people-search.component';
export * from './activiti-create-task-attachment.component';