diff --git a/demo-shell-ng2/app/components/activiti/activiti-demo.component.html b/demo-shell-ng2/app/components/activiti/activiti-demo.component.html index 022f38e635..b8e2a99653 100644 --- a/demo-shell-ng2/app/components/activiti/activiti-demo.component.html +++ b/demo-shell-ng2/app/components/activiti/activiti-demo.component.html @@ -162,6 +162,18 @@ (taskClick)="onProcessDetailsTaskClick($event)">
+
+ Process Audit log + +
+
+ assignment_ind + +``` + +![adf-process-audit-directive](docs/assets/adf-process-audit-directive.png) + +### Properties + +| Name | Type | Default | Description | +| --- | --- | --- | --- | +| taskId | string | | (**required**) The id of the task. | +| format | string | pdf | In whitch format you want the task audit information (pdf or json). | +| download | boolean | false | True If you want download the file on the click event. | +| fileName | string | Audit | Represent the name of the file to download in case the format is pdf. | + +#### Events + +| Name | Description | +| --- | --- | +| clicked | Raised when the task audit info is ready | +| error | Raised if there is an error during fetching task information | + ## Build from sources You can build component from sources with the following commands: diff --git a/ng2-components/ng2-activiti-processlist/docs/assets/adf-process-audit-directive.png b/ng2-components/ng2-activiti-processlist/docs/assets/adf-process-audit-directive.png new file mode 100644 index 0000000000..720d164b65 Binary files /dev/null and b/ng2-components/ng2-activiti-processlist/docs/assets/adf-process-audit-directive.png differ diff --git a/ng2-components/ng2-activiti-processlist/index.ts b/ng2-components/ng2-activiti-processlist/index.ts index 5dd5af071b..13caa3ef86 100644 --- a/ng2-components/ng2-activiti-processlist/index.ts +++ b/ng2-components/ng2-activiti-processlist/index.ts @@ -32,6 +32,7 @@ import { ProcessAttachmentListComponent } from './src/components/process-attachm import { ProcessCommentsComponent } from './src/components/process-comments.component'; import { ProcessFiltersComponent } from './src/components/process-filters.component'; import { ProcessInstanceDetailsComponent } from './src/components/process-instance-details.component'; +import { ProcessAuditDirective } from './src/components/process-audit.directive'; import { ProcessInstanceHeaderComponent } from './src/components/process-instance-header.component'; import { ProcessInstanceTasksComponent } from './src/components/process-instance-tasks.component'; import { ProcessInstanceVariablesComponent } from './src/components/process-instance-variables.component'; @@ -41,6 +42,7 @@ export {ProcessAttachmentListComponent} from './src/components/process-attachmen export {ProcessCommentsComponent} from './src/components/process-comments.component'; export {ProcessFiltersComponent} from './src/components/process-filters.component'; export {ProcessInstanceDetailsComponent} from './src/components/process-instance-details.component'; +export {ProcessAuditDirective} from './src/components/process-audit.directive'; export {ProcessInstanceHeaderComponent} from './src/components/process-instance-header.component'; export {ProcessInstanceTasksComponent} from './src/components/process-instance-tasks.component'; export {ProcessInstanceVariablesComponent} from './src/components/process-instance-variables.component'; @@ -87,6 +89,7 @@ export const ACTIVITI_PROCESSLIST_DIRECTIVES: [any] = [ ProcessInstanceListComponent, ProcessFiltersComponent, ProcessInstanceDetailsComponent, + ProcessAuditDirective, ProcessInstanceHeaderComponent, ProcessInstanceTasksComponent, ProcessInstanceVariablesComponent, diff --git a/ng2-components/ng2-activiti-processlist/src/components/process-audit.directive.spec.ts b/ng2-components/ng2-activiti-processlist/src/components/process-audit.directive.spec.ts new file mode 100644 index 0000000000..ba369c664c --- /dev/null +++ b/ng2-components/ng2-activiti-processlist/src/components/process-audit.directive.spec.ts @@ -0,0 +1,178 @@ +/*! + * @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 } from '@angular/core'; +import { + async, + ComponentFixture, + fakeAsync, + TestBed +} from '@angular/core/testing'; +import { CoreModule } from 'ng2-alfresco-core'; +import { Observable } from 'rxjs/Rx'; +import { ProcessService } from './../services/process.service'; +import { ProcessAuditDirective } from './process-audit.directive'; + +declare let jasmine: any; + +describe('ProcessAuditDirective', () => { + + let fixture: ComponentFixture; + let component: BasicButtonComponent; + let service: ProcessService; + + function createFakePdfBlob(): Blob { + let pdfData = atob( + 'JVBERi0xLjcKCjEgMCBvYmogICUgZW50cnkgcG9pbnQKPDwKICAvVHlwZSAvQ2F0YWxvZwog' + + 'IC9QYWdlcyAyIDAgUgo+PgplbmRvYmoKCjIgMCBvYmoKPDwKICAvVHlwZSAvUGFnZXMKICAv' + + 'TWVkaWFCb3ggWyAwIDAgMjAwIDIwMCBdCiAgL0NvdW50IDEKICAvS2lkcyBbIDMgMCBSIF0K' + + 'Pj4KZW5kb2JqCgozIDAgb2JqCjw8CiAgL1R5cGUgL1BhZ2UKICAvUGFyZW50IDIgMCBSCiAg' + + 'L1Jlc291cmNlcyA8PAogICAgL0ZvbnQgPDwKICAgICAgL0YxIDQgMCBSIAogICAgPj4KICA+' + + 'PgogIC9Db250ZW50cyA1IDAgUgo+PgplbmRvYmoKCjQgMCBvYmoKPDwKICAvVHlwZSAvRm9u' + + 'dAogIC9TdWJ0eXBlIC9UeXBlMQogIC9CYXNlRm9udCAvVGltZXMtUm9tYW4KPj4KZW5kb2Jq' + + 'Cgo1IDAgb2JqICAlIHBhZ2UgY29udGVudAo8PAogIC9MZW5ndGggNDQKPj4Kc3RyZWFtCkJU' + + 'CjcwIDUwIFRECi9GMSAxMiBUZgooSGVsbG8sIHdvcmxkISkgVGoKRVQKZW5kc3RyZWFtCmVu' + + 'ZG9iagoKeHJlZgowIDYKMDAwMDAwMDAwMCA2NTUzNSBmIAowMDAwMDAwMDEwIDAwMDAwIG4g' + + 'CjAwMDAwMDAwNzkgMDAwMDAgbiAKMDAwMDAwMDE3MyAwMDAwMCBuIAowMDAwMDAwMzAxIDAw' + + 'MDAwIG4gCjAwMDAwMDAzODAgMDAwMDAgbiAKdHJhaWxlcgo8PAogIC9TaXplIDYKICAvUm9v' + + 'dCAxIDAgUgo+PgpzdGFydHhyZWYKNDkyCiUlRU9G'); + return new Blob([pdfData], {type: 'application/pdf'}); + } + + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [CoreModule.forRoot()], + declarations: [BasicButtonComponent, ProcessAuditDirective], + providers: [ProcessService] + }); + + TestBed.compileComponents(); + + fixture = TestBed.createComponent(BasicButtonComponent); + component = fixture.componentInstance; + service = TestBed.get(ProcessService); + + jasmine.Ajax.install(); + })); + + afterEach(() => { + jasmine.Ajax.uninstall(); + }); + + it('should fetch the pdf Blob when the format is pdf', fakeAsync(() => { + component.fileName = 'FakeAuditName'; + component.format = 'pdf'; + let blob = createFakePdfBlob(); + spyOn(service, 'fetchProcessAuditPdfById').and.returnValue(Observable.of(blob)); + spyOn(component, 'onAuditClick').and.callThrough(); + + fixture.detectChanges(); + + let button = fixture.nativeElement.querySelector('#auditButton'); + + fixture.whenStable().then(() => { + fixture.detectChanges(); + expect(component.onAuditClick).toHaveBeenCalledWith({ format: 'pdf', value: blob, fileName: 'FakeAuditName' }); + }); + + button.click(); + + })); + + it('should fetch the json info when the format is json', fakeAsync(() => { + component.fileName = 'FakeAuditName'; + component.format = 'json'; + component.download = true; + const auditJson = { + processInstanceId: 42516, processInstanceName: 'Fake Process - August 3rd 2017', + processDefinitionName: 'Claim Approval Process', processDefinitionVersion: 1, processInstanceStartTime: 'Thu Aug 03 15:32:47 UTC 2017', processInstanceEndTime: null, + processInstanceDurationInMillis: null, + processInstanceInitiator: 'MyName MyLastname', + entries: [{ + index: 1, type: 'startForm', + selectedOutcome: null, formData: [{ + fieldName: 'User Name', + fieldId: 'username', value: 'dsassd' + }, + { fieldName: 'Claim Amount', fieldId: 'claimamount', value: '22' }], taskName: null, taskAssignee: null, activityId: null, + activityName: null, activityType: null, startTime: null, endTime: null, durationInMillis: null + } + ], decisionInfo: { calculatedValues: [], appliedRules: [] } + }; + spyOn(service, 'fetchProcessAuditJsonById').and.returnValue(Observable.of(auditJson)); + spyOn(component, 'onAuditClick').and.callThrough(); + + fixture.detectChanges(); + + let button = fixture.nativeElement.querySelector('#auditButton'); + + fixture.whenStable().then(() => { + fixture.detectChanges(); + expect(component.onAuditClick).toHaveBeenCalledWith({ format: 'json', value: auditJson, fileName: 'FakeAuditName' }); + }); + + button.click(); + + })); + + it('should fetch the pdf Blob as default when the format is UNKNOW', fakeAsync(() => { + component.fileName = 'FakeAuditName'; + component.format = 'fakeFormat'; + let blob = createFakePdfBlob(); + spyOn(service, 'fetchProcessAuditPdfById').and.returnValue(Observable.of(blob)); + spyOn(component, 'onAuditClick').and.callThrough(); + + fixture.detectChanges(); + + let button = fixture.nativeElement.querySelector('#auditButton'); + + fixture.whenStable().then(() => { + fixture.detectChanges(); + expect(component.onAuditClick).toHaveBeenCalledWith({ format: 'pdf', value: blob, fileName: 'FakeAuditName' }); + }); + + button.click(); + + })); + +}); + +@Component({ + selector: 'adf-basic-button', + template: ` + ` +}) +class BasicButtonComponent { + + download: boolean = false; + fileName: string; + format: string; + constructor() { + + } + + onAuditClick(event: any) { + console.log(event); + } +} diff --git a/ng2-components/ng2-activiti-processlist/src/components/process-audit.directive.ts b/ng2-components/ng2-activiti-processlist/src/components/process-audit.directive.ts new file mode 100644 index 0000000000..ce4f6f4edb --- /dev/null +++ b/ng2-components/ng2-activiti-processlist/src/components/process-audit.directive.ts @@ -0,0 +1,123 @@ +/*! + * @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 { Directive, EventEmitter, Input, OnChanges, OnInit, Output, SimpleChanges } from '@angular/core'; +import { ContentService } from 'ng2-alfresco-core'; +import { ProcessService } from './../services/process.service'; + +const JSON_FORMAT: string = 'json'; +const PDF_FORMAT: string = 'pdf'; + +@Directive({ + selector: 'button[adf-process-audit]', + host: { + 'role': 'button', + '(click)': 'onClickAudit()' + } +}) +export class ProcessAuditDirective implements OnInit, OnChanges { + + @Input('process-id') + processId: string; + + @Input() + fileName: string = 'Audit'; + + @Input() + format: string = 'pdf'; + + @Input() + download: boolean = true; + + @Output() + clicked: EventEmitter = new EventEmitter(); + + @Output() + error: EventEmitter = new EventEmitter(); + + public audit: any; + + /** + * + * @param translateService + * @param processListService + */ + constructor(private contentService: ContentService, + private processListService: ProcessService) { + } + + ngOnInit() { + console.log('OnInit'); + } + ngOnChanges(changes: SimpleChanges): void { + if (!this.isValidType()) { + this.setDefaultFormatType(); + } + } + + isValidType() { + if (this.format && (this.isJsonFormat() || this.isPdfFormat())) { + return true; + } + return false; + } + + setDefaultFormatType(): void { + this.format = PDF_FORMAT; + } + + /** + * fetch the audit information in the requested format + */ + fetchAuditInfo(): void { + if (this.isPdfFormat()) { + this.processListService.fetchProcessAuditPdfById(this.processId).subscribe( + (blob: Blob) => { + this.audit = blob; + if (this.download) { + this.contentService.downloadBlob(this.audit, this.fileName + '.pdf'); + } + this.clicked.emit({ format: this.format, value: this.audit, fileName: this.fileName }); + }, + (err) => { + this.error.emit(err); + }); + } else { + this.processListService.fetchProcessAuditJsonById(this.processId).subscribe( + (res) => { + this.audit = res; + this.clicked.emit({ format: this.format, value: this.audit, fileName: this.fileName }); + }, + (err) => { + this.error.emit(err); + }); + } + } + + onClickAudit() { + this.fetchAuditInfo(); + } + + isJsonFormat() { + return this.format === JSON_FORMAT; + } + + isPdfFormat() { + return this.format === PDF_FORMAT; + } + +} diff --git a/ng2-components/ng2-activiti-processlist/src/services/process.service.ts b/ng2-components/ng2-activiti-processlist/src/services/process.service.ts index c06570aed2..46724367eb 100644 --- a/ng2-components/ng2-activiti-processlist/src/services/process.service.ts +++ b/ng2-components/ng2-activiti-processlist/src/services/process.service.ts @@ -90,6 +90,24 @@ export class ProcessService extends TaskListService { }).catch(err => this.handleProcessError(err)); } + /** + * fetch the Process Audit information as a pdf + * @param processId - the process id + */ + fetchProcessAuditPdfById(processId: string): Observable { + return Observable.fromPromise(this.alfrescoApiService.getInstance().activiti.processApi.getProcessAuditPdf(processId)) + .catch(err => this.handleProcessError(err)); + } + + /** + * fetch the Process Audit information in a json format + * @param processId - the process id + */ + fetchProcessAuditJsonById(processId: string): Observable { + return Observable.fromPromise(this.alfrescoApiService.getInstance().activiti.processApi.getProcessAuditJson(processId)) + .catch(err => this.handleProcessError(err)); + } + /** * Create and return the default filters * @param appId