Add processlist component to demo shell

- Improvements to and rename list component
  - Add many more processlist components (filters, details, header,
    tasks)
  - Add dependency on tasklist component
  - Glue into demo-shell
  - Add Karma config but not actual tests yet

Refs #492
This commit is contained in:
Will Abson
2016-08-29 10:37:35 -04:00
parent 95eb606cc1
commit 3b87b450c0
32 changed files with 1441 additions and 47 deletions

View File

@@ -13,12 +13,12 @@
<div class="mdl-grid"> <div class="mdl-grid">
<div class="mdl-cell mdl-cell--2-col task-column mdl-shadow--2dp"> <div class="mdl-cell mdl-cell--2-col task-column mdl-shadow--2dp">
<span>Task Filters</span> <span>Task Filters</span>
<activiti-filters (filterClick)="onFilterClick($event)"></activiti-filters> <activiti-filters (filterClick)="onTaskFilterClick($event)"></activiti-filters>
</div> </div>
<div class="mdl-cell mdl-cell--3-col task-column mdl-shadow--2dp"> <div class="mdl-cell mdl-cell--3-col task-column mdl-shadow--2dp">
<span>Task List</span> <span>Task List</span>
<activiti-tasklist *ngIf="isTaskListSelected()" [taskFilter]="taskFilter" [schemaColumn]="schemaColumn" <activiti-tasklist *ngIf="isTaskListSelected()" [taskFilter]="taskFilter" [schemaColumn]="taskSchemaColumns"
(rowClick)="onRowClick($event)" #activititasklist></activiti-tasklist> (rowClick)="onTaskRowClick($event)" #activititasklist></activiti-tasklist>
</div> </div>
<div class="mdl-cell mdl-cell--7-col task-column mdl-shadow--2dp"> <div class="mdl-cell mdl-cell--7-col task-column mdl-shadow--2dp">
<span>Task Details</span> <span>Task Details</span>
@@ -33,12 +33,16 @@
<div class="mdl-grid"> <div class="mdl-grid">
<div class="mdl-cell mdl-cell--2-col task-column"> <div class="mdl-cell mdl-cell--2-col task-column">
<span>Process Filters</span> <span>Process Filters</span>
<activiti-process-filters (filterClick)="onProcessFilterClick($event)"></activiti-process-filters>
</div> </div>
<div class="mdl-cell mdl-cell--3-col task-column"> <div class="mdl-cell mdl-cell--3-col task-column">
<span>Process List</span> <span>Process List</span>
<activiti-process-instance-list [filter]="processFilter" [schemaColumn]="processSchemaColumns"
(rowClick)="onProcessRowClick($event)" #activitiprocesslist></activiti-process-instance-list>
</div> </div>
<div class="mdl-cell mdl-cell--7-col task-column"> <div class="mdl-cell mdl-cell--7-col task-column">
<span>Process Details</span> <span>Process Details</span>
<activiti-process-instance-details [processInstanceId]="currentProcessInstanceId" (taskFormCompleted)="taskFormCompleted()" (processCancelled)="processCancelled()" #activitiprocessdetails></activiti-process-instance-details>
</div> </div>
</div> </div>
</div> </div>

View File

@@ -17,6 +17,7 @@
import { Component, AfterViewChecked, ViewChild } from '@angular/core'; import { Component, AfterViewChecked, ViewChild } from '@angular/core';
import { ALFRESCO_TASKLIST_DIRECTIVES } from 'ng2-activiti-tasklist'; import { ALFRESCO_TASKLIST_DIRECTIVES } from 'ng2-activiti-tasklist';
import { ACTIVITI_PROCESSLIST_DIRECTIVES } from 'ng2-activiti-processlist';
import { ActivitiForm } from 'ng2-activiti-form'; import { ActivitiForm } from 'ng2-activiti-form';
declare let __moduleName: string; declare let __moduleName: string;
@@ -27,7 +28,7 @@ declare var componentHandler;
selector: 'activiti-demo', selector: 'activiti-demo',
templateUrl: './activiti-demo.component.html', templateUrl: './activiti-demo.component.html',
styleUrls: ['./activiti-demo.component.css'], styleUrls: ['./activiti-demo.component.css'],
directives: [ALFRESCO_TASKLIST_DIRECTIVES, ActivitiForm] directives: [ALFRESCO_TASKLIST_DIRECTIVES, ACTIVITI_PROCESSLIST_DIRECTIVES, ActivitiForm]
}) })
export class ActivitiDemoComponent implements AfterViewChecked { export class ActivitiDemoComponent implements AfterViewChecked {
@@ -39,11 +40,20 @@ export class ActivitiDemoComponent implements AfterViewChecked {
@ViewChild('activititasklist') @ViewChild('activititasklist')
activititasklist: any; activititasklist: any;
currentTaskId: string; @ViewChild('activitiprocesslist')
activitiprocesslist: any;
schemaColumn: any [] = []; @ViewChild('activitiprocessdetails')
activitiprocessdetails: any;
currentTaskId: string;
currentProcessInstanceId: string;
taskSchemaColumns: any [] = [];
processSchemaColumns: any [] = [];
taskFilter: any; taskFilter: any;
processFilter: any;
setChoice($event) { setChoice($event) {
this.currentChoice = $event.target.value; this.currentChoice = $event.target.value;
@@ -58,23 +68,44 @@ export class ActivitiDemoComponent implements AfterViewChecked {
} }
constructor() { constructor() {
console.log('Activiti demo component'); this.taskSchemaColumns = [
this.schemaColumn = [
{type: 'text', key: 'name', title: 'Name', cssClass: 'full-width name-column', sortable: true} {type: 'text', key: 'name', title: 'Name', cssClass: 'full-width name-column', sortable: true}
// {type: 'text', key: 'created', title: 'Created', sortable: true} // {type: 'text', key: 'created', title: 'Created', sortable: true}
]; ];
this.processSchemaColumns = [
{type: 'text', key: 'name', title: 'Name', cssClass: 'full-width name-column', sortable: true}
];
} }
onFilterClick(event: any) { onTaskFilterClick(event: any) {
this.taskFilter = event; this.taskFilter = event;
this.activititasklist.load(this.taskFilter); this.activititasklist.load(this.taskFilter);
} }
onRowClick(taskId) { onProcessFilterClick(event: any) {
this.processFilter = event.filter;
this.activitiprocesslist.load(this.processFilter);
}
onTaskRowClick(taskId) {
this.currentTaskId = taskId; this.currentTaskId = taskId;
this.activitidetails.loadDetails(this.currentTaskId); this.activitidetails.loadDetails(this.currentTaskId);
} }
onProcessRowClick(processInstanceId) {
this.currentProcessInstanceId = processInstanceId;
this.activitiprocessdetails.load(this.currentProcessInstanceId);
}
processCancelled(data: any) {
this.currentProcessInstanceId = null;
this.activitiprocesslist.reload();
}
taskFormCompleted(data: any) {
this.activitiprocesslist.reload();
}
ngAfterViewChecked() { ngAfterViewChecked() {
// workaround for MDL issues with dynamic components // workaround for MDL issues with dynamic components
if (componentHandler) { if (componentHandler) {

View File

@@ -20,6 +20,7 @@
'ng2-activiti-form': 'node_modules/ng2-activiti-form/dist', 'ng2-activiti-form': 'node_modules/ng2-activiti-form/dist',
'ng2-alfresco-viewer': 'node_modules/ng2-alfresco-viewer/dist', 'ng2-alfresco-viewer': 'node_modules/ng2-alfresco-viewer/dist',
'ng2-alfresco-webscript': 'node_modules/ng2-alfresco-webscript/dist', 'ng2-alfresco-webscript': 'node_modules/ng2-alfresco-webscript/dist',
'ng2-activiti-processlist': 'node_modules/ng2-activiti-processlist/dist',
'ng2-activiti-tasklist': 'node_modules/ng2-activiti-tasklist/dist' 'ng2-activiti-tasklist': 'node_modules/ng2-activiti-tasklist/dist'
}; };
// packages tells the System loader how to load when no filename and/or no extension // packages tells the System loader how to load when no filename and/or no extension
@@ -38,6 +39,7 @@
'ng2-alfresco-upload': { main: 'index.js', defaultExtension: 'js'}, 'ng2-alfresco-upload': { main: 'index.js', defaultExtension: 'js'},
'ng2-alfresco-viewer': { main: 'index.js', defaultExtension: 'js'}, 'ng2-alfresco-viewer': { main: 'index.js', defaultExtension: 'js'},
'ng2-activiti-form': { main: 'index.js', defaultExtension: 'js'}, 'ng2-activiti-form': { main: 'index.js', defaultExtension: 'js'},
'ng2-activiti-processlist': { main: 'index.js', defaultExtension: 'js'},
'ng2-activiti-tasklist': { main: 'index.js', defaultExtension: 'js'}, 'ng2-activiti-tasklist': { main: 'index.js', defaultExtension: 'js'},
'ng2-alfresco-webscript': { main: 'index.js', defaultExtension: 'js'} 'ng2-alfresco-webscript': { main: 'index.js', defaultExtension: 'js'}
}; };

View File

@@ -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 { DatePipe } from '@angular/common';
import {
DataTableAdapter, ObjectDataTableAdapter, ObjectDataColumn,
DataRow, DataColumn, DataSorting
} from 'ng2-alfresco-datatable';
export class ProcessListDataTableAdapter extends ObjectDataTableAdapter implements DataTableAdapter {
ERR_ROW_NOT_FOUND: string = 'Row not found';
ERR_COL_NOT_FOUND: string = 'Column not found';
DEFAULT_DATE_FORMAT: string = 'medium';
private sorting: DataSorting;
private rows: DataRow[];
private columns: DataColumn[];
constructor(rows: any, schema: DataColumn[]) {
super(rows, schema);
this.rows = rows;
this.columns = schema || [];
}
getRows(): Array<DataRow> {
return this.rows;
}
// TODO: disable this api
setRows(rows: Array<DataRow>) {
this.rows = rows || [];
this.sort();
}
getColumns(): Array<DataColumn> {
return this.columns;
}
setColumns(columns: Array<DataColumn>) {
this.columns = columns || [];
}
getValue(row: DataRow, col: DataColumn): any {
if (!row) {
throw new Error(this.ERR_ROW_NOT_FOUND);
}
if (!col) {
throw new Error(this.ERR_COL_NOT_FOUND);
}
let value = row.getValue(col.key);
if (col.type === 'date') {
let datePipe = new DatePipe();
let format = (<ActivitiDataColumn>(col)).format || this.DEFAULT_DATE_FORMAT;
try {
return datePipe.transform(value, format);
} catch (err) {
console.error(`Error parsing date ${value} to format ${format}`);
}
}
return value;
}
getSorting(): DataSorting {
return this.sorting;
}
setSorting(sorting: DataSorting): void {
this.sorting = sorting;
if (sorting && sorting.key && this.rows && this.rows.length > 0) {
this.rows.sort((a: DataRow, b: DataRow) => {
let left = a.getValue(sorting.key);
if (left) {
left = (left instanceof Date) ? left.valueOf().toString() : left.toString();
} else {
left = '';
}
let right = b.getValue(sorting.key);
if (right) {
right = (right instanceof Date) ? right.valueOf().toString() : right.toString();
} else {
right = '';
}
return sorting.direction === 'asc'
? left.localeCompare(right)
: right.localeCompare(left);
});
}
}
sort(key?: string, direction?: string): void {
let sorting = this.sorting || new DataSorting();
if (key) {
sorting.key = key;
sorting.direction = direction || 'asc';
}
this.setSorting(sorting);
}
}
export class ActivitiDataColumn extends ObjectDataColumn {
format: string;
}

View File

@@ -44,7 +44,7 @@ import {
<label for="token"><b>Insert a servicePath</b></label><br> <label for="token"><b>Insert a servicePath</b></label><br>
<input id="token" type="text" size="48" [(ngModel)]="servicePath"><br> <input id="token" type="text" size="48" [(ngModel)]="servicePath"><br>
<div class="container" *ngIf="authenticated"> <div class="container" *ngIf="authenticated">
<activiti-processlist></activiti-processlist> <activiti-process-instance-list></activiti-process-instance-list>
</div>`, </div>`,
providers: [ACTIVITI_PROCESSLIST_PROVIDERS], providers: [ACTIVITI_PROCESSLIST_PROVIDERS],
directives: [ACTIVITI_PROCESSLIST_DIRECTIVES] directives: [ACTIVITI_PROCESSLIST_DIRECTIVES]

View File

@@ -15,17 +15,30 @@
* limitations under the License. * limitations under the License.
*/ */
import { ActivitiProcesslistComponent } from './src/components/activiti-processlist.component'; import { ActivitiProcessInstanceListComponent } from './src/components/activiti-processlist.component';
import { ActivitiProcessFilters } from './src/components/activiti-filters.component';
import { ActivitiProcessInstanceHeader } from './src/components/activiti-process-instance-header.component';
import { ActivitiProcessInstanceTasks } from './src/components/activiti-process-instance-tasks.component';
import { ActivitiComments } from './src/components/activiti-comments.component';
import { ActivitiProcessInstanceDetails } from './src/components/activiti-process-instance-details.component';
import { ActivitiStartProcessButton } from './src/components/activiti-start-process.component';
import { ActivitiProcessService } from './src/services/activiti-process.service'; import { ActivitiProcessService } from './src/services/activiti-process.service';
// components // components
export * from './src/components/activiti-processlist.component'; export * from './src/components/activiti-processlist.component';
export * from './src/components/activiti-process-instance-details.component';
// services // services
export * from './src/services/activiti-process.service'; export * from './src/services/activiti-process.service';
export const ACTIVITI_PROCESSLIST_DIRECTIVES: [any] = [ export const ACTIVITI_PROCESSLIST_DIRECTIVES: [any] = [
ActivitiProcesslistComponent ActivitiProcessInstanceListComponent,
ActivitiProcessFilters,
ActivitiProcessInstanceDetails,
ActivitiProcessInstanceHeader,
ActivitiProcessInstanceTasks,
ActivitiComments,
ActivitiStartProcessButton
]; ];
export const ACTIVITI_PROCESSLIST_PROVIDERS: [any] = [ export const ACTIVITI_PROCESSLIST_PROVIDERS: [any] = [

View File

@@ -20,6 +20,9 @@ module.exports = function (config) {
{pattern: 'node_modules/ng2-alfresco-datatable/dist/**/*.js', included: false, served: true, watched: false}, {pattern: 'node_modules/ng2-alfresco-datatable/dist/**/*.js', included: false, served: true, watched: false},
{pattern: 'node_modules/ng2-alfresco-datatable/dist/**/*.html', included: false, served: true, watched: false}, {pattern: 'node_modules/ng2-alfresco-datatable/dist/**/*.html', included: false, served: true, watched: false},
{pattern: 'node_modules/ng2-alfresco-datatable/dist/**/*.css', included: false, served: true, watched: false}, {pattern: 'node_modules/ng2-alfresco-datatable/dist/**/*.css', included: false, served: true, watched: false},
{pattern: 'node_modules/ng2-activiti-tasklist/dist/**/*.js', included: false, served: true, watched: false},
{pattern: 'node_modules/ng2-activiti-tasklist/dist/**/*.html', included: false, served: true, watched: false},
{pattern: 'node_modules/ng2-activiti-tasklist/dist/**/*.css', included: false, served: true, watched: false},
{pattern: 'node_modules/ng2-translate/**/*.js', included: false, served: true, watched: false}, {pattern: 'node_modules/ng2-translate/**/*.js', included: false, served: true, watched: false},
{pattern: 'node_modules/alfresco-js-api/dist/alfresco-js-api.js', included: true, watched: false}, {pattern: 'node_modules/alfresco-js-api/dist/alfresco-js-api.js', included: true, watched: false},

View File

@@ -64,7 +64,8 @@
"zone.js": "^0.6.12", "zone.js": "^0.6.12",
"ng2-translate": "2.2.2", "ng2-translate": "2.2.2",
"ng2-alfresco-core": "0.2.0", "ng2-alfresco-core": "0.2.0",
"ng2-alfresco-datatable": "0.2.0" "ng2-alfresco-datatable": "0.2.0",
"ng2-activiti-tasklist": "0.2.0"
}, },
"devDependencies": { "devDependencies": {
"angular-cli": "1.0.0-beta.9", "angular-cli": "1.0.0-beta.9",

View File

@@ -0,0 +1,7 @@
:host {
width: 100%;
}
.activiti-label {
font-weight: bolder;
}

View File

@@ -0,0 +1,35 @@
<span class="activiti-label mdl-badge"
[attr.data-badge]="comments?.length">{{ 'DETAILS.LABELS.COMMENTS' |translate }}</span>
<div id="addComment" (click)="showDialog()" class="icon material-icons">add</div>
<div class="mdl-tooltip" for="addComment">
Add a comment
</div>
<div class="menu-container" *ngIf="comments?.length > 0">
<ul class='mdl-list'>
<li class="mdl-list__item" *ngFor="let comment of comments">
<span class="mdl-list__item-primary-content">
<i class="material-icons mdl-list__item-icon">comment</i>
{{comment.message}}
</span>
</li>
</ul>
</div>
<div *ngIf="comments?.length === 0">
{{ 'DETAILS.COMMENTS.NONE' | translate }}
</div>
<dialog class="mdl-dialog" #dialog>
<h4 class="mdl-dialog__title">New comment</h4>
<div class="mdl-dialog__content">
<div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label">
<textarea class="mdl-textfield__input" type="text" [(ngModel)]="message" rows="1" id="commentText"></textarea>
<label class="mdl-textfield__label" for="commentText">Message</label>
</div>
</div>
<div class="mdl-dialog__actions">
<button type="button" (click)="add()" class="mdl-button">Add Comment</button>
<button type="button" (click)="cancel()" class="mdl-button close">Cancel</button>
</div>
</dialog>

View File

@@ -0,0 +1,121 @@
/*!
* @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, Input, OnInit, ViewChild } from '@angular/core';
import { AlfrescoTranslationService, AlfrescoAuthenticationService, AlfrescoPipeTranslate } from 'ng2-alfresco-core';
import { ActivitiProcessService } from './../services/activiti-process.service';
import { Comment } from '../models/comment.model';
import { Observer } from 'rxjs/Observer';
import { Observable } from 'rxjs/Observable';
declare let componentHandler: any;
declare let __moduleName: string;
@Component({
selector: 'activiti-comments',
moduleId: __moduleName,
templateUrl: './activiti-comments.component.html',
styleUrls: ['./activiti-comments.component.css'],
providers: [ActivitiProcessService],
pipes: [ AlfrescoPipeTranslate ]
})
export class ActivitiComments implements OnInit {
@Input()
processId: string;
@ViewChild('dialog')
dialog: any;
comments: Comment [] = [];
private commentObserver: Observer<Comment>;
comment$: Observable<Comment>;
message: string;
/**
* Constructor
* @param auth
* @param translate
*/
constructor(private auth: AlfrescoAuthenticationService,
private translate: AlfrescoTranslationService,
private activitiProcess: ActivitiProcessService) {
if (translate) {
translate.addTranslationFolder('node_modules/ng2-activiti-processlist/src');
}
this.comment$ = new Observable<Comment>(observer => this.commentObserver = observer).share();
}
ngOnInit() {
this.comment$.subscribe((comment: Comment) => {
this.comments.push(comment);
});
if (this.processId) {
this.load(this.processId);
}
}
public load(taskId: string) {
this.comments = [];
if (this.processId) {
this.activitiProcess.getProcessInstanceComments(this.processId).subscribe(
(res: Comment[]) => {
res.forEach((comment) => {
this.commentObserver.next(comment);
});
},
(err) => {
console.log(err);
}
);
} else {
this.comments = [];
}
}
public showDialog() {
if (this.dialog) {
this.dialog.nativeElement.showModal();
}
}
public add() {
this.activitiProcess.addProcessInstanceComment(this.processId, this.message).subscribe(
(res: Comment) => {
this.comments.push(res);
this.message = '';
},
(err) => {
console.log(err);
}
);
this.cancel();
}
public cancel() {
if (this.dialog) {
this.dialog.nativeElement.close();
}
}
}

View File

@@ -0,0 +1,3 @@
.mdl-list__item {
cursor: pointer;
}

View File

@@ -0,0 +1,10 @@
<div class="menu-container">
<ul class='mdl-list'>
<li class="mdl-list__item"(click)="selectFilter(filter)" *ngFor="let filter of filters">
<span class="mdl-list__item-primary-content">
<i class="material-icons mdl-list__item-icon">assignment</i>
{{filter.name}}
</span>
</li>
</ul>
</div>

View File

@@ -0,0 +1,131 @@
/*!
* @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, Output, EventEmitter, OnInit, Input } from '@angular/core';
import { AlfrescoTranslationService, AlfrescoAuthenticationService, AlfrescoPipeTranslate } from 'ng2-alfresco-core';
import { ActivitiProcessService } from './../services/activiti-process.service';
import { FilterModel } from '../models/filter.model';
import { Observer } from 'rxjs/Observer';
import { Observable } from 'rxjs/Observable';
declare let componentHandler: any;
declare let __moduleName: string;
@Component({
selector: 'activiti-process-filters',
moduleId: __moduleName,
templateUrl: './activiti-filters.component.html',
styleUrls: ['activiti-filters.component.css'],
providers: [ActivitiProcessService],
pipes: [AlfrescoPipeTranslate]
})
export class ActivitiProcessFilters implements OnInit {
@Output()
filterClick: EventEmitter<FilterModel> = new EventEmitter<FilterModel>();
@Output()
onSuccess: EventEmitter<any> = new EventEmitter<any>();
@Output()
onError: EventEmitter<any> = new EventEmitter<any>();
@Input()
appId: string;
@Input()
appName: string;
private filterObserver: Observer<FilterModel>;
filter$: Observable<FilterModel>;
currentFilter: FilterModel;
filters: FilterModel [] = [];
/**
* Constructor
* @param auth
* @param translate
* @param activiti
*/
constructor(private auth: AlfrescoAuthenticationService,
private translate: AlfrescoTranslationService,
public activiti: ActivitiProcessService) {
this.filter$ = new Observable<FilterModel>(observer => this.filterObserver = observer).share();
if (translate) {
translate.addTranslationFolder('node_modules/ng2-activiti-processlist/src');
}
}
ngOnInit() {
this.filter$.subscribe((filter: FilterModel) => {
this.filters.push(filter);
});
this.load();
}
/**
* The method call the adapter data table component for render the task list
* @param tasks
*/
private load() {
if (this.appName) {
this.filterByAppName();
} else {
this.filterByAppId(this.appId);
}
}
private filterByAppId(appId) {
this.activiti.getProcessFilters(appId).subscribe(
(res: FilterModel[]) => {
res.forEach((filter) => {
this.filterObserver.next(filter);
});
this.onSuccess.emit(res);
},
(err) => {
console.log(err);
this.onError.emit(err);
}
);
}
private filterByAppName() {
this.activiti.getDeployedApplications(this.appName).subscribe(
application => {
this.filterByAppId(application.id);
},
(err) => {
console.log(err);
this.onError.emit(err);
});
}
/**
* Pass the selected filter as next
* @param filter
*/
public selectFilter(filter: FilterModel) {
this.currentFilter = filter;
this.filterClick.emit(filter);
}
}

View File

@@ -0,0 +1,13 @@
<div *ngIf="!processInstanceDetails">{{ 'DETAILS.MESSAGES.NONE'|translate }}</div>
<div *ngIf="processInstanceDetails">
<h2 class="mdl-card__title-text">{{processInstanceDetails.name}}</h2>
<activiti-process-instance-header [processInstance]="processInstanceDetails" (processCancelled)="processCancelled()" #activitiprocessheader></activiti-process-instance-header>
<div class="mdl-grid">
<div class="mdl-cell mdl-cell--8-col">
<activiti-process-instance-tasks [processId]="processInstanceDetails.id" (taskFormCompleted)="taskFormCompleted()" #activitiprocesstasks></activiti-process-instance-tasks>
</div>
<div class="mdl-cell mdl-cell--4-col">
<activiti-comments [processId]="processInstanceDetails.id" #activitiprocesscomments></activiti-comments>
</div>
</div>
</div>

View File

@@ -0,0 +1,109 @@
/*!
* @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, Input, ViewChild, Output, EventEmitter } from '@angular/core';
import { AlfrescoTranslationService, AlfrescoAuthenticationService, AlfrescoPipeTranslate } from 'ng2-alfresco-core';
import { ActivitiProcessService } from './../services/activiti-process.service';
import { ActivitiProcessInstanceHeader } from './activiti-process-instance-header.component';
import { ActivitiProcessInstanceTasks } from './activiti-process-instance-tasks.component';
import { ActivitiComments } from './activiti-comments.component';
import { ProcessInstance } from '../models/process-instance';
declare let componentHandler: any;
declare let __moduleName: string;
@Component({
selector: 'activiti-process-instance-details',
moduleId: __moduleName,
templateUrl: './activiti-process-instance-details.component.html',
styleUrls: ['./activiti-process-instance-details.component.css'],
providers: [ActivitiProcessService],
directives: [ActivitiProcessInstanceHeader, ActivitiComments, ActivitiProcessInstanceTasks],
pipes: [AlfrescoPipeTranslate]
})
export class ActivitiProcessInstanceDetails {
@Input()
processInstanceId: string;
@ViewChild('activitiprocessheader')
processInstanceHeader: ActivitiProcessInstanceHeader;
@ViewChild('activitiprocesstasks')
tasksList: ActivitiProcessInstanceTasks;
@ViewChild('activitiprocesscomments')
commentsList: ActivitiComments;
@Input()
showTitle: boolean = true;
@Input()
showRefreshButton: boolean = true;
@Output()
processCancelledEmitter = new EventEmitter();
@Output()
taskFormCompletedEmitter = new EventEmitter();
processInstanceDetails: ProcessInstance;
/**
* Constructor
* @param auth
* @param translate
* @param activitiProcess
*/
constructor(private auth: AlfrescoAuthenticationService,
private translate: AlfrescoTranslationService,
private activitiProcess: ActivitiProcessService) {
if (translate) {
translate.addTranslationFolder('node_modules/ng2-activiti-processlist/src');
}
}
load(processId: string) {
if (processId) {
this.activitiProcess.getProcess(processId).subscribe(
(res: ProcessInstance) => {
this.processInstanceDetails = res;
if (this.processInstanceDetails) {
if (this.commentsList) {
this.commentsList.load(this.processInstanceDetails.id);
}
if (this.tasksList) {
this.tasksList.load(this.processInstanceDetails.id);
}
}
console.log('Loaded process instance', this.processInstanceDetails);
}
);
}
}
processCancelled(data: any) {
this.processCancelledEmitter.emit(data);
}
taskFormCompleted(data: any) {
this.taskFormCompletedEmitter.emit(data);
}
}

View File

@@ -0,0 +1,7 @@
:host {
width: 100%;
}
.activiti-label {
font-weight: bolder;
}

View File

@@ -0,0 +1,15 @@
<div *ngIf="processInstance">
<div class="mdl-grid">
<div class="mdl-cell mdl-cell--4-col">
<span class="activiti-label">{{ 'DETAILS.LABELS.STARTED_BY' | translate }}</span>:
{{getStartedByFullName()}}
</div>
<div class="mdl-cell mdl-cell--4-col">
<span class="activiti-label">{{ 'DETAILS.LABELS.STARTED' | translate }}</span>:
{{getStartedDate() | date:'medium'}}
</div>
<div class="mdl-cell mdl-cell--4-col">
<button type="button" (click)="cancelProcess()" class="mdl-button">Cancel Process</button>
</div>
</div>
</div>

View File

@@ -0,0 +1,74 @@
/*!
* @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, Input, Output, EventEmitter } from '@angular/core';
import { AlfrescoTranslationService, AlfrescoAuthenticationService, AlfrescoPipeTranslate } from 'ng2-alfresco-core';
import { ProcessInstance } from '../models/process-instance';
import { ActivitiProcessService } from './../services/activiti-process.service';
declare let componentHandler: any;
declare let __moduleName: string;
@Component({
selector: 'activiti-process-instance-header',
moduleId: __moduleName,
templateUrl: './activiti-process-instance-header.component.html',
styleUrls: ['./activiti-process-instance-header.component.css'],
pipes: [ AlfrescoPipeTranslate ]
})
export class ActivitiProcessInstanceHeader {
@Input()
processInstance: ProcessInstance;
@Output()
processCancelled = new EventEmitter();
/**
* Constructor
* @param auth
* @param translate
* @param activitiProcess
*/
constructor(private auth: AlfrescoAuthenticationService,
private translate: AlfrescoTranslationService,
private activitiProcess: ActivitiProcessService) {
if (translate) {
translate.addTranslationFolder('node_modules/ng2-activiti-tasklist/src');
}
}
getStartedByFullName() {
if (this.processInstance && this.processInstance.startedBy) {
return (this.processInstance.startedBy.firstName && this.processInstance.startedBy.firstName !== 'null'
? this.processInstance.startedBy.firstName + ' ' : '') +
this.processInstance.startedBy.lastName;
}
return '';
}
getStartedDate() {
return this.processInstance ? new Date(this.processInstance.started) : null;
}
cancelProcess() {
console.log('Cancel process', this.processInstance);
this.processCancelled.emit(this.activitiProcess.cancelProcess(this.processInstance.id));
}
}

View File

@@ -0,0 +1,19 @@
:host {
width: 100%;
}
.activiti-label {
font-weight: bolder;
}
.material-icons:hover {
color: rgb(255, 152, 0);
}
.task-details-dialog {
width: 600px;
}
.process-tasks-refresh {
float: right;
}

View File

@@ -0,0 +1,52 @@
<div *ngIf="showRefreshButton" class="process-tasks-refresh" >
<button (click)="onRefreshClicked()"
class="mdl-button mdl-button--icon mdl-js-button mdl-js-ripple-effect">
<i class="material-icons">refresh</i>
</button>
</div>
<span class="activiti-label mdl-badge"
[attr.data-badge]="activeTasks?.length">{{ 'DETAILS.LABELS.TASKS_ACTIVE'|translate }}</span>
<div class="menu-container" *ngIf="activeTasks?.length > 0">
<ul class='mdl-list'>
<li class="mdl-list__item mdl-list__item--two-line" *ngFor="let task of activeTasks">
<span class="mdl-list__item-primary-content" (click)="clickTask($event, task)">
<i class="material-icons mdl-list__item-icon">assignment</i>
<span>{{task.name}}</span>
<span class="mdl-list__item-sub-title">Assigned to {{getUserFullName(task.assignee)}}, created {{task.created | date:'mediumDate'}}</span>
</span>
</li>
</ul>
</div>
<div *ngIf="activeTasks?.length === 0">
{{ 'DETAILS.TASKS.NO_ACTIVE' | translate }}
</div>
<span class="activiti-label mdl-badge"
[attr.data-badge]="completedTasks?.length">{{ 'DETAILS.LABELS.TASKS_COMPLETED'|translate }}</span>
<div class="menu-container" *ngIf="completedTasks?.length > 0">
<ul class='mdl-list'>
<li class="mdl-list__item mdl-list__item--two-line" *ngFor="let task of completedTasks">
<span class="mdl-list__item-primary-content" (click)="clickTask($event, task)">
<i class="material-icons mdl-list__item-icon">assignment</i>
<span>{{task.name}}</span>
<span class="mdl-list__item-sub-title">Assigned to {{getUserFullName(task.assignee)}}, created {{task.created | date:'mediumDate'}}</span>
</span>
</li>
</ul>
</div>
<div *ngIf="completedTasks?.length === 0">
{{ 'DETAILS.TASKS.NO_COMPLETED' | translate }}
</div>
<dialog class="mdl-dialog task-details-dialog" #dialog>
<h4 class="mdl-dialog__title">Task details</h4>
<div class="mdl-dialog__content">
<activiti-task-details [taskId]="selectedTaskId" (formCompleted)="taskFormCompleted()" #taskdetails></activiti-task-details>
</div>
<div class="mdl-dialog__actions">
<button type="button" (click)="cancelDialog()" class="mdl-button close">Close</button>
</div>
</dialog>

View File

@@ -0,0 +1,183 @@
/*!
* @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, Input, OnInit, ViewChild, Output, EventEmitter } from '@angular/core';
import { AlfrescoTranslationService, AlfrescoAuthenticationService, AlfrescoPipeTranslate } from 'ng2-alfresco-core';
import { ActivitiProcessService } from './../services/activiti-process.service';
import { TaskDetailsModel } from '../models/task-details.model';
import { ALFRESCO_TASKLIST_DIRECTIVES } from 'ng2-activiti-tasklist';
import { Observer } from 'rxjs/Observer';
import { Observable } from 'rxjs/Observable';
declare let componentHandler: any;
declare let __moduleName: string;
@Component({
selector: 'activiti-process-instance-tasks',
moduleId: __moduleName,
templateUrl: './activiti-process-instance-tasks.component.html',
styleUrls: ['./activiti-process-instance-tasks.component.css'],
providers: [ActivitiProcessService],
directives: [ ALFRESCO_TASKLIST_DIRECTIVES ],
pipes: [ AlfrescoPipeTranslate ]
})
export class ActivitiProcessInstanceTasks implements OnInit {
@Input()
processId: string;
@Input()
showRefreshButton: boolean = true;
@Output()
taskFormCompletedEmitter = new EventEmitter();
activeTasks: TaskDetailsModel[] = [];
completedTasks: TaskDetailsModel[] = [];
private taskObserver: Observer<TaskDetailsModel>;
private completedTaskObserver: Observer<TaskDetailsModel>;
task$: Observable<TaskDetailsModel>;
completedTask$: Observable<TaskDetailsModel>;
message: string;
selectedTaskId: string;
@ViewChild('dialog')
dialog: any;
@ViewChild('taskdetails')
taskdetails: any;
/**
* Constructor
* @param auth
* @param translate
* @param activitiProcess
*/
constructor(private auth: AlfrescoAuthenticationService,
private translate: AlfrescoTranslationService,
private activitiProcess: ActivitiProcessService) {
if (translate) {
translate.addTranslationFolder('node_modules/ng2-activiti-processlist/src');
}
this.task$ = new Observable<TaskDetailsModel>(observer => this.taskObserver = observer).share();
this.completedTask$ = new Observable<TaskDetailsModel>(observer => this.completedTaskObserver = observer).share();
}
ngOnInit() {
this.task$.subscribe((task: TaskDetailsModel) => {
this.activeTasks.push(task);
});
this.completedTask$.subscribe((task: TaskDetailsModel) => {
this.completedTasks.push(task);
});
if (this.processId) {
this.load(this.processId);
}
}
public load(processId: string) {
this.loadActive(processId);
this.loadCompleted(processId);
}
public loadActive(processId: string) {
this.activeTasks = [];
if (processId) {
this.activitiProcess.getProcessTasks(processId, null).subscribe(
(res: TaskDetailsModel[]) => {
res.forEach((task) => {
this.taskObserver.next(task);
});
},
(err) => {
console.log(err);
}
);
} else {
this.activeTasks = [];
}
}
public loadCompleted(processId: string) {
this.completedTasks = [];
if (processId) {
this.activitiProcess.getProcessTasks(processId, 'completed').subscribe(
(res: TaskDetailsModel[]) => {
res.forEach((task) => {
this.completedTaskObserver.next(task);
});
},
(err) => {
console.log(err);
}
);
} else {
this.completedTasks = [];
}
}
getUserFullName(user: any) {
if (user) {
return (user.firstName && user.firstName !== 'null'
? user.firstName + ' ' : '') +
user.lastName;
}
return '';
}
public clickTask($event: any, task: TaskDetailsModel) {
console.log('selected task', task);
this.selectedTaskId = task.id;
this.taskdetails.loadDetails(task.id);
this.showDialog();
}
public showDialog() {
if (this.dialog) {
this.dialog.nativeElement.showModal();
}
}
public cancelDialog() {
this.closeDialog();
}
private closeDialog() {
if (this.dialog) {
this.dialog.nativeElement.close();
}
}
public taskFormCompleted() {
this.closeDialog();
this.load(this.processId);
this.taskFormCompletedEmitter.emit(this.processId);
}
public onRefreshClicked() {
this.load(this.processId);
}
}

View File

@@ -1,6 +1,12 @@
<h1>My Activiti Processes</h1> <div *ngIf="!filter">{{ 'FILTERS.MESSAGES.NONE' | translate }}</div>
<div *ngIf="filter">
<p *ngIf="processInstances && processInstances.length == 0">{{ 'PROCESSLIST.NONE' | translate }}</p> <div *ngIf="!isListEmpty()">
<alfresco-datatable <alfresco-datatable
[data]="data"> [data]="data"
</alfresco-datatable> (rowClick)="onRowClick($event)">
</alfresco-datatable>
</div>
<div *ngIf="isListEmpty()">
{{ 'PROCESSLIST.NONE' | translate }}
</div>
</div>

View File

@@ -18,7 +18,7 @@
import {describe, expect, it, inject, beforeEachProviders, beforeEach} from '@angular/core/testing'; import {describe, expect, it, inject, beforeEachProviders, beforeEach} from '@angular/core/testing';
import {TestComponentBuilder} from '@angular/compiler/testing'; import {TestComponentBuilder} from '@angular/compiler/testing';
import {AlfrescoSettingsService, AlfrescoTranslationService, AlfrescoAuthenticationService} from 'ng2-alfresco-core'; import {AlfrescoSettingsService, AlfrescoTranslationService, AlfrescoAuthenticationService} from 'ng2-alfresco-core';
import {ActivitiProcesslistComponent} from '../../src/components/activiti-processlist.component'; import {ActivitiProcessInstanceListComponent} from '../../src/components/activiti-processlist.component';
import {TranslationMock} from './../assets/translation.service.mock'; import {TranslationMock} from './../assets/translation.service.mock';
import {ActivitiProcessService} from '../services/activiti-process.service'; import {ActivitiProcessService} from '../services/activiti-process.service';
@@ -38,7 +38,7 @@ describe('ActivitiProcesslistComponent', () => {
beforeEach(inject([TestComponentBuilder], (tcb: TestComponentBuilder) => { beforeEach(inject([TestComponentBuilder], (tcb: TestComponentBuilder) => {
return tcb return tcb
.createAsync(ActivitiProcesslistComponent) .createAsync(ActivitiProcessInstanceListComponent)
.then(fixture => { .then(fixture => {
processlistComponentFixture = fixture; processlistComponentFixture = fixture;
element = processlistComponentFixture.nativeElement; element = processlistComponentFixture.nativeElement;

View File

@@ -15,17 +15,17 @@
* limitations under the License. * limitations under the License.
*/ */
import {Component, OnInit } from '@angular/core'; import {Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
import { AlfrescoPipeTranslate, AlfrescoTranslationService, CONTEXT_MENU_DIRECTIVES, CONTEXT_MENU_PROVIDERS } from 'ng2-alfresco-core'; import { AlfrescoPipeTranslate, AlfrescoTranslationService, CONTEXT_MENU_DIRECTIVES, CONTEXT_MENU_PROVIDERS } from 'ng2-alfresco-core';
import { ALFRESCO_DATATABLE_DIRECTIVES, ObjectDataTableAdapter } from 'ng2-alfresco-datatable'; import { ALFRESCO_DATATABLE_DIRECTIVES, ObjectDataTableAdapter, DataRowEvent } from 'ng2-alfresco-datatable';
import { ActivitiProcessService } from '../services/activiti-process.service'; import { ActivitiProcessService } from '../services/activiti-process.service';
import { ProcessInstance } from '../models/process-instance'; import { FilterModel } from '../models/filter.model';
declare let __moduleName: string; declare let __moduleName: string;
@Component({ @Component({
moduleId: __moduleName, moduleId: __moduleName,
selector: 'activiti-processlist', selector: 'activiti-process-instance-list',
styles: [ styles: [
` `
:host h1 { :host h1 {
@@ -36,13 +36,33 @@ declare let __moduleName: string;
templateUrl: './activiti-processlist.component.html', templateUrl: './activiti-processlist.component.html',
directives: [ ALFRESCO_DATATABLE_DIRECTIVES, CONTEXT_MENU_DIRECTIVES ], directives: [ ALFRESCO_DATATABLE_DIRECTIVES, CONTEXT_MENU_DIRECTIVES ],
pipes: [ AlfrescoPipeTranslate ], pipes: [ AlfrescoPipeTranslate ],
providers: [ CONTEXT_MENU_PROVIDERS ] providers: [ CONTEXT_MENU_PROVIDERS, ActivitiProcessService ]
}) })
export class ActivitiProcesslistComponent implements OnInit { export class ActivitiProcessInstanceListComponent implements OnInit {
errorMessage: string; errorMessage: string;
processInstances: ProcessInstance[];
data: ObjectDataTableAdapter; data: ObjectDataTableAdapter;
currentProcessInstanceId: string;
@Input()
filter: FilterModel;
@Input()
schemaColumn: any[] = [
{type: 'text', key: 'id', title: 'Id', sortable: true},
{type: 'text', key: 'name', title: 'Name', cssClass: 'full-width name-column', sortable: true},
{type: 'text', key: 'started', title: 'Started', sortable: true},
{type: 'text', key: 'startedBy.email', title: 'Started By', sortable: true}
];
@Output()
rowClick: EventEmitter<string> = new EventEmitter<string>();
@Output()
onSuccess: EventEmitter<any> = new EventEmitter<any>();
@Output()
onError: EventEmitter<any> = new EventEmitter<any>();
constructor (private processService: ActivitiProcessService, private translate: AlfrescoTranslationService) { constructor (private processService: ActivitiProcessService, private translate: AlfrescoTranslationService) {
if (translate !== null) { if (translate !== null) {
@@ -51,27 +71,77 @@ export class ActivitiProcesslistComponent implements OnInit {
} }
ngOnInit() { ngOnInit() {
this.getProcesses(); this.data = new ObjectDataTableAdapter(
[],
this.schemaColumn
);
if (this.filter) {
this.load(this.filter);
}
} }
getProcesses() { load(filter: FilterModel) {
this.processService.getProcesses() this.processService.getProcessInstances(filter)
.subscribe( .subscribe(
(processInstances) => { (processInstances) => {
this.data = new ObjectDataTableAdapter( this.renderProcessInstances(processInstances);
processInstances, this.onSuccess.emit(processInstances);
[
{type: 'text', key: 'id', title: 'Id', sortable: true},
{type: 'text', key: 'name', title: 'Name', cssClass: 'full-width name-column', sortable: true},
{type: 'text', key: 'started', title: 'Started', sortable: true},
{type: 'text', key: 'startedBy.email', title: 'Started By', sortable: true}
]
);
}, },
error => this.errorMessage = <any>error); error => {
this.errorMessage = <any>error;
this.onError.emit(error);
});
} }
onItemClick(processInstance: ProcessInstance, event: any) { reload() {
console.log(processInstance, event); this.load(this.filter);
}
/**
* Render the process list
*
* @param processInstances
*/
private renderProcessInstances(processInstances: any[]) {
processInstances = this.optimizeProcessNames(processInstances);
this.data = new ObjectDataTableAdapter(
processInstances,
this.schemaColumn
);
}
/**
* Check if the list is empty
* @returns {ObjectDataTableAdapter|boolean}
*/
isListEmpty(): boolean {
return this.data === undefined ||
(this.data && this.data.getRows() && this.data.getRows().length === 0);
}
/**
* Emit the event rowClick passing the current task id when the row is clicked
* @param event
*/
onRowClick(event: DataRowEvent) {
let item = event;
this.currentProcessInstanceId = item.value.getValue('id');
this.rowClick.emit(this.currentProcessInstanceId);
}
/**
* Optimize process name field
* @param tasks
* @returns {any[]}
*/
private optimizeProcessNames(tasks: any[]) {
tasks = tasks.map(t => {
t.name = t.name || 'No name';
if (t.name.length > 50) {
t.name = t.name.substring(0, 50) + '...';
}
return t;
});
return tasks;
} }
} }

View File

@@ -1,10 +1,34 @@
{ {
"PROCESSLIST": { "PROCESSLIST": {
"NONE": "No active processes were found", "NONE": "No process instances were found",
"SUMMARY": "Found {{total}} active process instances", "SUMMARY": "Found {{total}} process instances",
"ERROR": "An error occurred while loading the processes: {{errorMessage}}", "ERROR": "An error occurred while loading the processes instances: {{errorMessage}}",
"COLUMN": { "COLUMN": {
"NAME": "Name" "NAME": "Name"
} }
},
"FILTERS": {
"MESSAGES": {
"NONE": "No process instance filter selected."
}
},
"DETAILS": {
"LABELS": {
"STARTED_BY": "Started by",
"STARTED": "Started",
"COMMENTS": "Comments",
"TASKS_ACTIVE": "Active Tasks",
"TASKS_COMPLETED": "Completed Tasks"
},
"MESSAGES": {
"NONE": "No process details found."
},
"TASKS": {
"NO_ACTIVE": "No tasks are currently active",
"NO_COMPLETED": "No tasks have been completed yet"
},
"COMMENTS": {
"NONE": "No comments."
}
} }
} }

View File

@@ -0,0 +1,38 @@
/*!
* @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.
*/
/**
*
* Comment submitted against a process
*
* @returns {Comment} .
*/
import { User } from './user.model';
export class Comment {
id: number;
message: string;
created: string;
createdBy: User;
constructor(id: number, message: string, created: string, createdBy: User) {
this.id = id;
this.message = message;
this.created = created;
this.createdBy = createdBy;
}
}

View File

@@ -0,0 +1,60 @@
/*!
* @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.
*/
/**
*
* This object represent the filter.
*
*
* @returns {FilterModel} .
*/
export class FilterModel {
id: number;
name: string;
recent: boolean = false;
icon: string;
filter: FilterParamsModel;
appId: number;
constructor(name: string, recent: boolean, icon: string, query: string, state: string, assignment: string, appDefinitionId?: string) {
this.name = name;
this.recent = recent;
this.icon = icon;
this.filter = new FilterParamsModel(query, state, assignment, appDefinitionId);
}
}
/**
*
* This object represent the parameters of a filter.
*
*
* @returns {FilterModel} .
*/
export class FilterParamsModel {
name: string;
sort: string;
state: string;
appDefinitionId: string;
constructor(query: string, sort: string, state: string, appDefinitionId?: string) {
this.name = query;
this.sort = sort;
this.state = state;
this.appDefinitionId = appDefinitionId;
}
}

View File

@@ -0,0 +1,93 @@
/*!
* @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.
*/
/**
*
* This object represent the details of a task.
*
*
* @returns {TaskDetailsModel} .
*/
import { User } from './user.model';
export class TaskDetailsModel {
id: string;
name: string;
assignee: User;
priority: number;
adhocTaskCanBeReassigned: number;
category: string;
created: string;
description: string;
dueDate: string;
duration: string;
endDate: string;
executionId: string;
formKey: string;
initiatorCanCompleteTask: boolean = false;
managerOfCandidateGroup: boolean = false;
memberOfCandidateGroup: boolean = false;
memberOfCandidateUsers: boolean = false;
involvedPeople: User [];
parentTaskId: string;
parentTaskName: string;
processDefinitionCategory: string;
processDefinitionDeploymentId: string;
processDefinitionDescription: string;
processDefinitionId: string;
processDefinitionKey: string;
processDefinitionName: string;
processDefinitionVersion: number = 0;
processInstanceId: string;
processInstanceName: string;
processInstanceStartUserId: string;
taskDefinitionKey: string;
constructor(obj: any) {
this.id = obj.id;
this.name = obj.name;
this.priority = obj.priority;
this.assignee = new User(obj.assignee.id, obj.assignee.email, obj.assignee.firstName, obj.assignee.lastName);
this.adhocTaskCanBeReassigned = obj.adhocTaskCanBeReassigned;
this.created = obj.created;
this.description = obj.description;
this.dueDate = obj.dueDate;
this.duration = obj.duration;
this.endDate = obj.endDate;
this.executionId = obj.executionId;
this.formKey = obj.formKey;
this.initiatorCanCompleteTask = obj.initiatorCanCompleteTask;
this.managerOfCandidateGroup = obj.managerOfCandidateGroup;
this.memberOfCandidateGroup = obj.memberOfCandidateGroup;
this.memberOfCandidateUsers = obj.memberOfCandidateUsers;
this.involvedPeople = obj.involvedPeople;
this.parentTaskId = obj.parentTaskId;
this.parentTaskName = obj.parentTaskName;
this.processDefinitionCategory = obj.processDefinitionCategory;
this.processDefinitionDeploymentId = obj.processDefinitionDeploymentId;
this.processDefinitionDescription = obj.processDefinitionDescription;
this.processDefinitionId = obj.processDefinitionId;
this.processDefinitionKey = obj.processDefinitionKey;
this.processDefinitionName = obj.processDefinitionName;
this.processDefinitionVersion = obj.processDefinitionVersion;
this.processInstanceId = obj.processInstanceId;
this.processInstanceName = obj.processInstanceName;
this.processInstanceStartUserId = obj.processInstanceStartUserId;
this.taskDefinitionKey = obj.taskDefinitionKey;
}
}

View File

@@ -0,0 +1,38 @@
/*!
* @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.
*/
/**
*
* This object represent the user.
*
*
* @returns {User} .
*/
export class User {
id: number;
email: string;
firstName: string;
lastName: string;
constructor(id: number, email: string, firstName: string, lastName: string) {
this.id = id;
this.email = email;
this.firstName = firstName;
this.lastName = lastName;
}
}

View File

@@ -17,6 +17,9 @@
import {AlfrescoAuthenticationService} from 'ng2-alfresco-core'; import {AlfrescoAuthenticationService} from 'ng2-alfresco-core';
import {ProcessInstance} from '../models/process-instance'; import {ProcessInstance} from '../models/process-instance';
import {FilterModel} from '../models/filter.model';
import {User} from '../models/user.model';
import {Comment} from '../models/comment.model';
import {Injectable} from '@angular/core'; import {Injectable} from '@angular/core';
import {Observable} from 'rxjs/Observable'; import {Observable} from 'rxjs/Observable';
import 'rxjs/add/operator/map'; import 'rxjs/add/operator/map';
@@ -28,6 +31,16 @@ export class ActivitiProcessService {
constructor(public authService: AlfrescoAuthenticationService) { constructor(public authService: AlfrescoAuthenticationService) {
} }
/**
* Retrive all the Deployed app
* @returns {Observable<any>}
*/
getDeployedApplications(name: string): Observable<any> {
return Observable.fromPromise(this.authService.getAlfrescoApi().activiti.appsApi.getAppDefinitions())
.map((response: any) => response.data.find(p => p.name === name))
.do(data => console.log('Application: ' + JSON.stringify(data)));
}
getProcesses(): Observable<ProcessInstance[]> { getProcesses(): Observable<ProcessInstance[]> {
let request = {'page': 0, 'sort': 'created-desc', 'state': 'all'}; let request = {'page': 0, 'sort': 'created-desc', 'state': 'all'};
@@ -36,6 +49,99 @@ export class ActivitiProcessService {
.catch(this.handleError); .catch(this.handleError);
} }
getProcessInstances(filter: FilterModel): Observable<ProcessInstance[]> {
return Observable.fromPromise(this.authService.getAlfrescoApi().activiti.processApi.getProcessInstances(filter))
.map(this.extractData)
.catch(this.handleError);
}
getProcessFilters(appId: string): Observable<any[]> {
let filterOpts = appId ? {
appId: appId
} : {};
return Observable.fromPromise(this.authService.getAlfrescoApi().activiti.userFiltersApi.getUserProcessInstanceFilters(filterOpts))
.map(this.extractData)
.catch(this.handleError);
}
getProcess(id: string): Observable<ProcessInstance> {
return Observable.fromPromise(this.authService.getAlfrescoApi().activiti.processApi.getProcessInstance(id))
.catch(this.handleError);
}
getProcessTasks(id: string, state: string): Observable<any[]> {
let taskOpts = state ? {
processInstanceId: id,
state: state
} : {
processInstanceId: id
};
return Observable.fromPromise(this.authService.getAlfrescoApi().activiti.taskApi.listTasks(taskOpts))
.map(this.extractData)
.map(tasks => tasks.map((task: any) => {
task.created = new Date(task.created);
return task;
}))
.catch(this.handleError);
}
/**
* Retrive all the process instance's comments
* @param id - process instance ID
* @returns {<Comment[]>}
*/
getProcessInstanceComments(id: string): Observable<Comment[]> {
return Observable.fromPromise(this.authService.getAlfrescoApi().activiti.commentsApi.getProcessInstanceComments(id))
.map(res => res)
.map((response: any) => {
let comments: Comment[] = [];
response.data.forEach((comment) => {
let user = new User(
comment.createdBy.id, comment.createdBy.email, comment.createdBy.firstName, comment.createdBy.lastName);
comments.push(new Comment(comment.id, comment.message, comment.created, user));
});
return comments;
}).catch(this.handleError);
}
/**
* Add a comment to a process instance
* @param id - process instance Id
* @param message - content of the comment
* @returns {Comment}
*/
addProcessInstanceComment(id: string, message: string): Observable<Comment> {
return Observable.fromPromise(
this.authService.getAlfrescoApi().activiti.commentsApi.addProcessInstanceComment({message: message}, id)
)
.map(res => res)
.map((response: Comment) => {
return new Comment(response.id, response.message, response.created, response.createdBy);
}).catch(this.handleError);
}
getProcessDefinitions(appId: string) {
let opts = appId ? {
latest: true,
appId: appId
} : {
latest: true
};
return Observable.fromPromise(
this.authService.getAlfrescoApi().activiti.processApi.getProcessDefinitions(opts)
)
.map(this.extractData)
.catch(this.handleError);
}
cancelProcess(processInstanceId: string) {
return Observable.fromPromise(
this.authService.getAlfrescoApi().activiti.processApi.deleteProcessInstance(processInstanceId)
)
.catch(this.handleError);
}
private extractData(res: any) { private extractData(res: any) {
return res.data || {}; return res.data || {};
} }