1、工作流基本代码编写

2、activiti-starter排除Security配置引起的跨域问题
3、前端添加bpm相关代码
pull/254/head
xjs 3 years ago
parent 0f6ad79c8c
commit f69a1049c5

@ -9,6 +9,8 @@ import com.ruoyi.system.api.domain.SysUser;
import com.ruoyi.system.api.factory.RemoteUserFallbackFactory; import com.ruoyi.system.api.factory.RemoteUserFallbackFactory;
import com.ruoyi.system.api.model.LoginUser; import com.ruoyi.system.api.model.LoginUser;
import java.util.List;
/** /**
* <br> * <br>
* remote * remote
@ -47,4 +49,8 @@ public interface RemoteUserService
@PutMapping("/user/updateForRPC/{id}") @PutMapping("/user/updateForRPC/{id}")
R<Integer> updateForRPC(@PathVariable("id") String id,@RequestBody Integer count,@RequestHeader(SecurityConstants.FROM_SOURCE) String source); R<Integer> updateForRPC(@PathVariable("id") String id,@RequestBody Integer count,@RequestHeader(SecurityConstants.FROM_SOURCE) String source);
@GetMapping("/user/selectUserNameByPostCodeAndDeptId")
R<List<String>> selectUserNameByPostCodeAndDeptId(@RequestParam("postCode") String postCode,
@RequestParam("deptId") Long deptId);
} }

@ -9,9 +9,11 @@ import com.ruoyi.system.api.RemoteUserService;
import com.ruoyi.system.api.domain.SysUser; import com.ruoyi.system.api.domain.SysUser;
import com.ruoyi.system.api.model.LoginUser; import com.ruoyi.system.api.model.LoginUser;
import java.util.List;
/** /**
* *
* *
* @author ruoyi * @author ruoyi
*/ */
@Component @Component
@ -41,6 +43,11 @@ public class RemoteUserFallbackFactory implements FallbackFactory<RemoteUserServ
public R<Integer> updateForRPC(String id, Integer count, String source) { public R<Integer> updateForRPC(String id, Integer count, String source) {
return R.fail("修改用户登录次数失败:" + throwable.getMessage()); return R.fail("修改用户登录次数失败:" + throwable.getMessage());
} }
@Override
public R<List<String>> selectUserNameByPostCodeAndDeptId(String postCode, Long deptId) {
return R.fail("selectUserNameByPostCodeAndDeptId失败:" + throwable.getMessage());
}
}; };
} }
} }

@ -53,6 +53,12 @@ public class SysUserController extends BaseController
@Autowired @Autowired
private ISysConfigService configService; private ISysConfigService configService;
@GetMapping("/selectUserNameByPostCodeAndDeptId")
public R<List<String>> selectUserNameByPostCodeAndDeptId(@RequestParam("postCode") String postCode,
@RequestParam("deptId") Long deptId) {
return R.ok(userService.selectUserNameByPostCodeAndDeptId(postCode,deptId));
}
/** /**
* *
*/ */
@ -153,7 +159,7 @@ public class SysUserController extends BaseController
/** /**
* *
* *
* @return * @return
*/ */
@GetMapping("getInfo") @GetMapping("getInfo")

@ -7,14 +7,14 @@ import java.util.List;
/** /**
* *
* *
* @author ruoyi * @author ruoyi
*/ */
public interface SysUserMapper public interface SysUserMapper
{ {
/** /**
* *
* *
* @param sysUser * @param sysUser
* @return * @return
*/ */
@ -22,7 +22,7 @@ public interface SysUserMapper
/** /**
* *
* *
* @param user * @param user
* @return * @return
*/ */
@ -30,7 +30,7 @@ public interface SysUserMapper
/** /**
* *
* *
* @param user * @param user
* @return * @return
*/ */
@ -38,7 +38,7 @@ public interface SysUserMapper
/** /**
* *
* *
* @param userName * @param userName
* @return * @return
*/ */
@ -46,7 +46,7 @@ public interface SysUserMapper
/** /**
* ID * ID
* *
* @param userId ID * @param userId ID
* @return * @return
*/ */
@ -54,7 +54,7 @@ public interface SysUserMapper
/** /**
* *
* *
* @param user * @param user
* @return * @return
*/ */
@ -62,7 +62,7 @@ public interface SysUserMapper
/** /**
* *
* *
* @param user * @param user
* @return * @return
*/ */
@ -70,7 +70,7 @@ public interface SysUserMapper
/** /**
* *
* *
* @param userName * @param userName
* @param avatar * @param avatar
* @return * @return
@ -79,7 +79,7 @@ public interface SysUserMapper
/** /**
* *
* *
* @param userName * @param userName
* @param password * @param password
* @return * @return
@ -88,7 +88,7 @@ public interface SysUserMapper
/** /**
* ID * ID
* *
* @param userId ID * @param userId ID
* @return * @return
*/ */
@ -96,7 +96,7 @@ public interface SysUserMapper
/** /**
* *
* *
* @param userIds ID * @param userIds ID
* @return * @return
*/ */
@ -104,7 +104,7 @@ public interface SysUserMapper
/** /**
* *
* *
* @param userName * @param userName
* @return * @return
*/ */
@ -133,4 +133,6 @@ public interface SysUserMapper
* @return int * @return int
*/ */
int updateUserLoginCount(@Param("id")String id, @Param("count")Integer count); int updateUserLoginCount(@Param("id")String id, @Param("count")Integer count);
List<String>selectUserNameByPostCodeAndDeptId(String postCode,Long deptId);
} }

@ -13,7 +13,7 @@ public interface ISysUserService
{ {
/** /**
* *
* *
* @param user * @param user
* @return * @return
*/ */
@ -21,7 +21,7 @@ public interface ISysUserService
/** /**
* *
* *
* @param user * @param user
* @return * @return
*/ */
@ -29,7 +29,7 @@ public interface ISysUserService
/** /**
* *
* *
* @param user * @param user
* @return * @return
*/ */
@ -37,7 +37,7 @@ public interface ISysUserService
/** /**
* *
* *
* @param userName * @param userName
* @return * @return
*/ */
@ -45,7 +45,7 @@ public interface ISysUserService
/** /**
* ID * ID
* *
* @param userId ID * @param userId ID
* @return * @return
*/ */
@ -53,7 +53,7 @@ public interface ISysUserService
/** /**
* ID * ID
* *
* @param userName * @param userName
* @return * @return
*/ */
@ -61,7 +61,7 @@ public interface ISysUserService
/** /**
* ID * ID
* *
* @param userName * @param userName
* @return * @return
*/ */
@ -69,7 +69,7 @@ public interface ISysUserService
/** /**
* *
* *
* @param userName * @param userName
* @return * @return
*/ */
@ -93,21 +93,21 @@ public interface ISysUserService
/** /**
* *
* *
* @param user * @param user
*/ */
public void checkUserAllowed(SysUser user); public void checkUserAllowed(SysUser user);
/** /**
* *
* *
* @param userId id * @param userId id
*/ */
public void checkUserDataScope(Long userId); public void checkUserDataScope(Long userId);
/** /**
* *
* *
* @param user * @param user
* @return * @return
*/ */
@ -115,7 +115,7 @@ public interface ISysUserService
/** /**
* *
* *
* @param user * @param user
* @return * @return
*/ */
@ -123,7 +123,7 @@ public interface ISysUserService
/** /**
* *
* *
* @param user * @param user
* @return * @return
*/ */
@ -131,7 +131,7 @@ public interface ISysUserService
/** /**
* *
* *
* @param userId ID * @param userId ID
* @param roleIds * @param roleIds
*/ */
@ -139,7 +139,7 @@ public interface ISysUserService
/** /**
* *
* *
* @param user * @param user
* @return * @return
*/ */
@ -147,7 +147,7 @@ public interface ISysUserService
/** /**
* *
* *
* @param user * @param user
* @return * @return
*/ */
@ -155,7 +155,7 @@ public interface ISysUserService
/** /**
* *
* *
* @param userName * @param userName
* @param avatar * @param avatar
* @return * @return
@ -164,7 +164,7 @@ public interface ISysUserService
/** /**
* *
* *
* @param user * @param user
* @return * @return
*/ */
@ -172,7 +172,7 @@ public interface ISysUserService
/** /**
* *
* *
* @param userName * @param userName
* @param password * @param password
* @return * @return
@ -181,7 +181,7 @@ public interface ISysUserService
/** /**
* ID * ID
* *
* @param userId ID * @param userId ID
* @return * @return
*/ */
@ -189,7 +189,7 @@ public interface ISysUserService
/** /**
* *
* *
* @param userIds ID * @param userIds ID
* @return * @return
*/ */
@ -197,7 +197,7 @@ public interface ISysUserService
/** /**
* *
* *
* @param userList * @param userList
* @param isUpdateSupport * @param isUpdateSupport
* @param operName * @param operName
@ -212,4 +212,6 @@ public interface ISysUserService
* @return int * @return int
*/ */
int updateUserLoginCount(String id, Integer count); int updateUserLoginCount(String id, Integer count);
List<String>selectUserNameByPostCodeAndDeptId(String postCode,Long deptId);
} }

@ -563,4 +563,8 @@ public class SysUserServiceImpl implements ISysUserService
return userMapper.updateUserLoginCount(id,count); return userMapper.updateUserLoginCount(id,count);
} }
public List<String>selectUserNameByPostCodeAndDeptId(String postCode,Long deptId){
return userMapper.selectUserNameByPostCodeAndDeptId(postCode,deptId);
}
} }

@ -209,7 +209,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<update id="resetUserPwd" parameterType="SysUser"> <update id="resetUserPwd" parameterType="SysUser">
update sys_user set password = #{password} where user_name = #{userName} update sys_user set password = #{password} where user_name = #{userName}
</update> </update>
<delete id="deleteUserById" parameterType="Long"> <delete id="deleteUserById" parameterType="Long">
update sys_user set del_flag = '2' where user_id = #{userId} update sys_user set del_flag = '2' where user_id = #{userId}
</delete> </delete>
@ -227,5 +227,12 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
update sys_user set login_count = #{count} where user_id= #{id} update sys_user set login_count = #{count} where user_id= #{id}
</update> </update>
<select id="selectUserNameByPostCodeAndDeptId" resultType="String">
SELECT user_name FROM sys_post p
left join sys_user_post up on p.post_id = up.post_id
left join sys_user u on up.user_id= u.user_id
where post_code=#{param1} and dept_id=#{param2}
</mapper> </select>
</mapper>

@ -0,0 +1,38 @@
import request from '@/utils/request'
// 查询modeler列表
export function listDefinition(query) {
return request({
url: '/workflow/processDefinition/list',
method: 'get',
params: query
})
}
// 查询请假详细
export function getDefinitionsByInstanceId(instanceId) {
return request({
url: '/workflow/processDefinition/getDefinitions/' + instanceId,
method: 'get'
})
}
// 挂起激活转换
export function suspendOrActiveApply(data) {
return request({
url: '/workflow/processDefinition/suspendOrActiveApply',
method: 'post',
data:data
})
}
// 删除Modeler
export function delDefinition(id) {
return request({
url: '/workflow/processDefinition/remove/' + id,
method: 'delete'
})
}

@ -0,0 +1,8 @@
import request from '@/utils/request'
// 查询请假详细
export function historyFromData(instanceId) {
return request({
url: '/workflow/historyFromData/ByInstanceId/' + instanceId,
method: 'get'
})
}

@ -0,0 +1,31 @@
import request from '@/utils/request'
// 查询task列表
export function listTask(query) {
return request({
url: '/workflow/task/list',
method: 'get',
params: query
})
}
// 查询表单
export function formDataShow(taskID) {
return request({
url: '/workflow/task/formDataShow/'+taskID,
method: 'get',
})
}
// 查询表单
export function formDataSave(taskID,data) {
return request({
url: '/workflow/task/formDataSave/'+taskID,
method: 'post',
data:data
})
}

@ -0,0 +1,62 @@
import request from '@/utils/request'
// 查询请假列表
export function listLeave(query) {
return request({
url: '/workflow/workflow/leave/list',
method: 'get',
params: query
})
}
// 查询请假列表
export function listLeaveAll(query) {
return request({
url: '/workflow/workflow/leave/listAll',
method: 'get',
params: query
})
}
// 查询请假详细
export function getLeave(id) {
return request({
url: '/workflow/workflow/leave/' + id,
method: 'get'
})
}
// 新增请假
export function addLeave(data) {
return request({
url: '/workflow/workflow/leave',
method: 'post',
data: data
})
}
// 修改请假
export function updateLeave(data) {
return request({
url: '/workflow/workflow/leave',
method: 'put',
data: data
})
}
// 删除请假
export function delLeave(id) {
return request({
url: '/workflow/workflow/leave/' + id,
method: 'delete'
})
}
// 导出请假
export function exportLeave(query) {
return request({
url: '/workflow/workflow/leave/export',
method: 'get',
params: query
})
}

@ -0,0 +1,291 @@
<template>
<div class="app-container">
<el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="88px">
<el-form-item label="流程KEY" prop="key">
<el-input
v-model="queryParams.key"
placeholder="请输入流程KEY"
clearable
size="small"
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="名称" prop="name">
<el-input
v-model="queryParams.name"
placeholder="请输入名称"
clearable
size="small"
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item>
<el-button type="cyan" icon="el-icon-search" size="mini" @click="handleQuery"></el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery"></el-button>
</el-form-item>
</el-form>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button
type="primary"
icon="el-icon-plus"
size="mini"
@click="OnlineDrawingProcess"
v-hasPermi="['activiti:modeler']"
>在线绘制流程
</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="primary"
icon="el-icon-plus"
size="mini"
@click="handleImport"
v-hasPermi="['activiti:modeler']"
>部署流程
</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<el-table v-loading="loading" :data="Modeler">
<el-table-column label="流程ID" align="center" prop="id"/>
<el-table-column label="流程KEY" align="center" prop="key"/>
<el-table-column label="流程名称" align="center" prop="name"/>
<el-table-column label="版本" align="center" prop="version"/>
<el-table-column label="部署时间" align="center" prop="deploymentTime"/>
<el-table-column label="部署ID" align="center" prop="deploymentId"/>
<el-table-column label="状态" align="center" prop="suspendState">
<template slot-scope="scope">
<span>{{ scope.row.suspendState!=1?'挂起':'激活'}}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template slot-scope="scope">
<el-button
size="mini"
type="text"
icon="el-icon-edit"
@click="OnlineModificationProcess(scope.row)"
v-hasPermi="['activiti:modeler']"
>查看
</el-button>
<el-button
size="mini"
type="text"
icon="el-icon-edit"
@click="suspendOrActiveApply(scope.row)"
v-hasPermi="['activiti:modeler']"
>{{scope.row.suspendState==1?'挂起':'激活'}}
</el-button>
<el-button
size="mini"
type="text"
icon="el-icon-delete"
@click="handleDelete(scope.row)"
v-hasPermi="['activiti:modeler']"
>删除
</el-button>
</template>
</el-table-column>
</el-table>
<pagination
v-show="total>0"
:total="total"
:page.sync="queryParams.pageNum"
:limit.sync="queryParams.pageSize"
@pagination="getList"
/>
<!--bpmnjs在线流程设计器-->
<el-dialog
:visible.sync="modelVisible"
title="流程图"
width="1680px"
@close="modelCancel"
append-to-body
>
<div style="position:relative;height: 100%;">
<iframe
id="iframe"
:src="modelerUrl"
frameborder="0"
width="100%"
height="720px"
scrolling="auto"
></iframe>
</div>
</el-dialog>
<!-- 用户导入对话框 -->
<el-dialog :title="upload.title" :visible.sync="upload.open" width="400px" append-to-body>
<el-upload ref="upload" :limit="1" accept=".bpmn, .bar, .zip" :headers="upload.headers" :action="upload.url"
:disabled="upload.isUploading" :on-progress="handleFileUploadProgress" :on-success="handleFileSuccess"
:auto-upload="false" drag>
<i class="el-icon-upload"></i>
<div class="el-upload__text">
将文件拖到此处
<em>点击上传</em>
</div>
<div class="el-upload__tip" style="color:red" slot="tip">提示仅允许导入bpmnbarzip格式文件</div>
</el-upload>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitFileForm"> </el-button>
<el-button @click="upload.open = false"> </el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import {getToken} from "@/utils/auth";
import {delDefinition, listDefinition, suspendOrActiveApply} from "@/api/business/workflow/activiti/definition";
export default {
name: "ActIdGroup",
data() {
return {
modelerUrl: "",
modelVisible: false,
//
loading: true,
//
single: true,
//
multiple: true,
//
showSearch: true,
//
total: 0,
// VIEW
Modeler: [],
//
title: "",
//
open: false,
src: "",
//
queryParams: {
pageNum: 1,
pageSize: 10,
id: null,
rev: null,
name: null,
type: null
},
upload: {
//
open: false,
//
title: "",
//
isUploading: false,
//
headers: {Authorization: "Bearer " + getToken()},
//
url: process.env.VUE_APP_BASE_API + "/processDefinition/uploadStreamAndDeployment",
},
};
},
created() {
this.getList();
},
methods: {
/** 查询Definition列表 */
getList() {
this.loading = true;
listDefinition(this.queryParams).then(response => {
this.Modeler = response.rows;
this.total = response.total;
this.loading = false;
});
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNum = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm("queryForm");
this.handleQuery();
},
suspendOrActiveApply(row) {
var suspendOrActive = row.suspendState === '2' ? '激活' : '挂起';
this.$confirm('确认要' + suspendOrActive + '流程定义吗?', "警告", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning"
}).then(function () {
var data = {"id": row.id, "suspendState": row.suspendState};
return suspendOrActiveApply(data);
}).then(() => {
this.getList();
this.msgSuccess("转换成功");
}).catch(function () {
});
},
handleImport() {
this.upload.title = "上传模型图";
this.upload.open = true;
},
OnlineDrawingProcess() {
this.modelVisible = true;
localStorage.setItem("VUE_APP_BASE_API", process.env.VUE_APP_BASE_API)
this.modelerUrl = "/bpmnjs/index.html?type=addBpmn";
},
OnlineModificationProcess(data) {
this.modelVisible = true;
localStorage.setItem("VUE_APP_BASE_API", process.env.VUE_APP_BASE_API)
this.modelerUrl = '/bpmnjs/index.html?type=lookBpmn&deploymentFileUUID=' + data.deploymentId + '&deploymentName=' + encodeURI(data.resourceName);
},
//
submitFileForm() {
this.$refs.upload.submit();
},
//
handleFileUploadProgress(event, file, fileList) {
this.upload.isUploading = true;
},
//
handleFileSuccess(response, file, fileList) {
this.upload.open = false;
this.upload.isUploading = false;
this.$refs.upload.clearFiles();
this.$alert(response.msg, "导入结果", {dangerouslyUseHTMLString: true});
this.getList();
},
/** 删除按钮操作 */
handleDelete(row) {
this.$confirm('是否确认删除编号为"' + row.key + '"的数据项?', "警告", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning"
}).then(function () {
return delDefinition(row.deploymentId);
}).then(() => {
this.getList();
this.msgSuccess("删除成功");
}).catch(function () {
});
},
modelCancel() {
this.getList();
}
}
};
</script>

@ -0,0 +1,188 @@
<template>
<div class="app-container">
<el-table v-loading="loading" :data="tastList">
<!-- <el-table-column label="流程ID" align="center" prop="id"/>-->
<el-table-column label="流程名称" align="center" prop="instanceName"/>
<el-table-column label="任务节点名称" align="center" prop="name"/>
<el-table-column label="任务状态" align="center" prop="status"/>
<el-table-column label="办理人" align="center" prop="assignee"/>
<el-table-column label="创建时间" align="center" prop="createdDate"/>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template slot-scope="scope">
<el-button
size="mini"
type="text"
icon="el-icon-edit"
@click="examineAndApprove (scope.row)"
v-hasPermi="['workflow:leave:edit']"
>审批
</el-button>
</template>
</el-table-column>
</el-table>
<pagination
v-show="total>0"
:total="total"
:page.sync="queryParams.pageNum"
:limit.sync="queryParams.pageSize"
@pagination="getList"
/>
<!-- 审批对话框 -->
<el-dialog :title="title" :visible.sync="open" v-if="open" width="500px" append-to-body>
<leaveHistoryForm :businessKey="businessKey" v-if="'leave'==definitionKey"/>
<el-form :model="form" ref="form" label-width="100px" class="demo-dynamic">
<el-form-item
v-for="(domain, index) in form.formData"
:label="domain.controlLable"
:key="index"
>
<el-radio-group v-model="domain.controlValue" v-if="'radio'==domain.controlType">
<el-radio v-for="(defaults,indexd) in domain.controlDefault.split('--__--')"
:label=indexd
:key="indexd"
>{{defaults}}
</el-radio>
</el-radio-group>
<el-input type="textarea" v-model="domain.controlValue" v-if="'textarea'==domain.controlType"
></el-input>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import leaveHistoryForm from "@/views/business/workflow/workflow/leave/leaveHistoryForm";
import {formDataSave, formDataShow, listTask} from "@/api/business/workflow/activiti/task";
export default {
name: "Leave",
components: {leaveHistoryForm},
data() {
return {
id:'',
definitionKey: '',
businessKey: '',
//
loading: true,
//
ids: [],
//
single: true,
//
multiple: true,
//
showSearch: true,
//
total: 0,
//
tastList: [],
//
title: "",
//
open: false,
//
queryParams: {
pageNum: 1,
pageSize: 10,
},
//
form: {
formData:[]
},
//
rules: {}
};
},
created() {
this.getList();
},
methods: {
/** 查询请假列表 */
getList() {
this.loading = true;
listTask(this.queryParams).then(response => {
this.tastList = response.rows;
this.total = response.total;
this.loading = false;
});
},
//
cancel() {
this.open = false;
this.reset();
},
//
reset() {
this.definitionKey = '',
this.businessKey = '',
this.form = {
formData:[],
};
this.resetForm("form");
},
/** 审批按钮操作 */
examineAndApprove(row) {
console.log(row)
this.reset();
this.definitionKey = row.definitionKey;
this.businessKey = row.businessKey;
this.id=row.id;
formDataShow(row.id).then(response => {
// FormProperty_3qipis2--__!!radio--__!!--__!!i--__!!--__--
// FormProperty_0lffpcm--__!!textarea--__!!--__!!f--__!!null
let datas = response.data;
let formData = []
for (let i = 0; i < datas.length; i++) {
let strings = datas[i].split('--__!!')
let controlValue = null
let controlDefault = null
switch (strings[1]) {
case 'radio':
controlValue = 0;
controlDefault = strings[4]
break;
// default:
}
formData.push({
controlId: strings[0],
controlType: strings[1],
controlLable: strings[2],
controlIsParam: strings[3],
controlValue: controlValue,
controlDefault: controlDefault
})
}
this.form.formData = formData;
this.open = true;
this.title = "审批";
});
},
/** 提交按钮 */
submitForm() {
formDataSave(this.id,this.form.formData).then(response => {
this.msgSuccess("审批成功");
this.open = false;
this.getList();
});
},
}
};
</script>

@ -0,0 +1,459 @@
<template>
<div class="app-container">
<el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item label="请假类型" prop="type">
<el-select v-model="queryParams.type" placeholder="请选择请假类型" clearable size="small">
<el-option
v-for="dict in typeOptions"
:key="dict.dictValue"
:label="dict.dictLabel"
:value="dict.dictValue"
/>
</el-select>
</el-form-item>
<el-form-item label="标题" prop="title">
<el-input
v-model="queryParams.title"
placeholder="请输入标题"
clearable
size="small"
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="状态" prop="state">
<el-select v-model="queryParams.state" placeholder="请选择状态" clearable size="small">
<el-option
v-for="dict in stateOptions"
:key="dict.dictValue"
:label="dict.dictLabel"
:value="dict.dictValue"
/>
</el-select>
</el-form-item>
<!-- <el-form-item label="创建者" prop="createBy">-->
<!-- <el-select v-model="queryParams.createBy" placeholder="请选择创建者" clearable size="small">-->
<!-- <el-option label="请选择字典生成" value="" />-->
<!-- </el-select>-->
<!-- </el-form-item>-->
<el-form-item>
<el-button type="cyan" icon="el-icon-search" size="mini" @click="handleQuery"></el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery"></el-button>
</el-form-item>
</el-form>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button
type="primary"
icon="el-icon-plus"
size="mini"
@click="handleAdd"
v-hasPermi="['workflow:leave:add']"
>新增
</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="warning"
icon="el-icon-download"
size="mini"
@click="handleExport"
v-hasPermi="['workflow:leave:export']"
>导出
</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<el-table v-loading="loading" :data="leaveList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center"/>
<!-- <el-table-column label="主键ID" align="center" prop="id" />-->
<el-table-column label="请假类型" align="center" prop="type" :formatter="typeFormat"/>
<el-table-column label="标题" align="center" prop="title"/>
<el-table-column label="原因" align="center" prop="reason"/>
<el-table-column label="开始时间" align="center" prop="leaveStartTime" width="180">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.leaveStartTime, '{y}-{m}-{d}') }}</span>
</template>
</el-table-column>
<el-table-column label="结束时间" align="center" prop="leaveEndTime" width="180">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.leaveEndTime, '{y}-{m}-{d}') }}</span>
</template>
</el-table-column>
<!-- <el-table-column label="状态" align="center" prop="state" :formatter="stateFormat">-->
<el-table-column label="状态" align="center">
<template slot-scope="scope">
<div v-if="scope.row.state!==0">
{{ stateFormat(scope.row) }}
</div>
<div v-else>
{{ scope.row.taskName }}
</div>
</template>
</el-table-column>
<!-- <el-table-column label="创建者" align="center" prop="createName" />-->
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template slot-scope="scope">
<el-button v-if="2===scope.row.state"
size="mini"
type="text"
icon="el-icon-edit"
@click="handleUpdate(scope.row)"
v-hasPermi="['workflow:leave:edit']"
>修改
</el-button>
<!-- <el-button v-if="1==scope.row.state"-->
<!-- size="mini"-->
<!-- type="text"-->
<!-- icon="el-icon-edit"-->
<!-- @click="terminateLeave(scope.row)"-->
<!-- v-hasPermi="['workflow:leave:edit']"-->
<!-- >销假-->
<!-- </el-button>-->
<el-button
size="mini"
type="text"
icon="el-icon-edit"
@click="historyFory(scope.row)"
v-hasPermi="['workflow:leave:edit']"
>审批详情
</el-button>
<el-button v-if="0===scope.row.state"
size="mini"
type="text"
icon="el-icon-edit"
@click="checkTheSchedule(scope.row)"
v-hasPermi="['workflow:leave:edit']"
>查看进度
</el-button>
</template>
</el-table-column>
</el-table>
<pagination
v-show="total>0"
:total="total"
:page.sync="queryParams.pageNum"
:limit.sync="queryParams.pageSize"
@pagination="getList"
/>
<el-dialog
:visible.sync="modelVisible"
title="进度查询"
width="1680px"
append-to-body
>
<div style="position:relative;height: 100%;">
<iframe
id="iframe"
:src="modelerUrl"
frameborder="0"
width="100%"
height="720px"
scrolling="auto"
></iframe>
</div>
</el-dialog>
<!-- 查看详细信息话框 -->
<el-dialog :title="title" :visible.sync="open2" width="500px" append-to-body>
<leaveHistoryForm :businessKey="businessKey" v-if="open2"/>
<div slot="footer" class="dialog-footer">
<el-button @click="open2=!open2"></el-button>
</div>
</el-dialog>
<el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
<el-form ref="form" :model="form" :rules="rules" label-width="80px">
<el-form-item label="请假类型" prop="type">
<el-select v-model="form.type" placeholder="请选择请假类型" @change="chooseMedicine">
<el-option
v-for="dict in typeOptions"
:key="dict.dictValue"
:label="dict.dictLabel"
:value="dict.dictValue"
></el-option>
</el-select>
</el-form-item>
<el-form-item label="标题" prop="title">
<el-input v-model="form.title"/>
</el-form-item>
<el-form-item label="原因" prop="reason">
<el-input v-model="form.reason" type="textarea" placeholder="请输入内容"/>
</el-form-item>
<el-form-item label="开始时间" prop="leaveStartTime">
<el-date-picker clearable size="small" style="width: 200px"
v-model="form.leaveStartTime"
type="date"
value-format="yyyy-MM-dd"
placeholder="选择开始时间">
</el-date-picker>
</el-form-item>
<el-form-item label="结束时间" prop="leaveEndTime">
<el-date-picker clearable size="small" style="width: 200px"
v-model="form.leaveEndTime"
type="date"
value-format="yyyy-MM-dd"
placeholder="选择结束时间">
</el-date-picker>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import leaveHistoryForm from "./leaveHistoryForm";
import {
addLeave,
delLeave,
exportLeave,
getLeave,
listLeave,
updateLeave
} from "@/api/business/workflow/workflow/leave";
import {getDefinitionsByInstanceId} from "@/api/business/workflow/activiti/definition";
export default {
name: 'Leave',
components: {leaveHistoryForm},
data() {
return {
modelVisible: false,
modelerUrl: '',
userName: '',
createName: '',
businessKey: '',
//
user: {},
//
loading: true,
//
ids: [],
//
single: true,
//
multiple: true,
//
showSearch: true,
//
total: 0,
//
leaveList: [],
//
title: '',
//
open: false,
open2: false,
//
typeOptions: [],
//
stateOptions: [],
//
queryParams: {
pageNum: 1,
pageSize: 10,
type: null,
title: null,
reason: null,
leaveStartTime: null,
leaveEndTime: null,
instanceId: null,
state: null,
createBy: null
},
//
form: {},
//
rules: {
type: [
{required: true, message: '请假类型不能为空', trigger: 'change'}
],
title: [
{required: true, message: '标题不能为空', trigger: 'blur'}
],
reason: [
{required: true, message: '原因不能为空', trigger: 'blur'}
],
leaveStartTime: [
{required: true, message: '开始时间不能为空', trigger: 'blur'}
],
leaveEndTime: [
{required: true, message: '结束时间不能为空', trigger: 'blur'}
]
}
}
},
created() {
this.getList()
this.getDicts('activiti_leave_type').then(response => {
this.typeOptions = response.data
})
this.getDicts('activiti_flow_type').then(response => {
this.stateOptions = response.data
})
},
methods: {
/** 查询请假列表 */
getList() {
this.loading = true
listLeave(this.queryParams).then(response => {
this.leaveList = response.rows
this.total = response.total
this.loading = false
})
},
//
typeFormat(row, column) {
return this.selectDictLabel(this.typeOptions, row.type)
},
//
stateFormat(row, column) {
return this.selectDictLabel(this.stateOptions, row.state)
},
//
cancel() {
this.open = false
this.reset()
},
//
reset() {
this.form = {
id: null,
type: null,
title: null,
reason: null,
leaveStartTime: null,
leaveEndTime: null,
instanceId: null,
state: null,
createBy: null,
createTime: null,
updateTime: null
}
this.resetForm('form')
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNum = 1
this.getList()
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm('queryForm')
this.handleQuery()
},
//
handleSelectionChange(selection) {
this.ids = selection.map(item => item.id)
this.single = selection.length !== 1
this.multiple = !selection.length
},
/** 新增按钮操作 */
handleAdd() {
this.createName = this.$store.getters.nickName
if (this.$store.getters.name !== "admin") {
this.reset()
this.open = true
this.title = '添加请假'
} else {
this.$alert('管理员不能创建流程', '管理员不能创建流程', {
confirmButtonText: '确定',
});
}
},
/** 修改按钮操作 */
handleUpdate(row) {
this.reset()
getLeave(row.id).then(response => {
this.form = response.data
this.open = true
this.title = '修改请假'
})
},
/** 审批详情 */
historyFory(row) {
this.businessKey = row.id
this.open2 = true
this.title = '审批详情'
},
/** 进度查看 */
checkTheSchedule(row) {
getDefinitionsByInstanceId(row.instanceId).then(response => {
let data = response.data
// this.url = '/bpmnjs/index.html?type=lookBpmn&deploymentFileUUID='+data.deploymentID+'&deploymentName='+ encodeURI(data.resourceName);
this.modelerUrl = '/bpmnjs/index.html?type=lookBpmn&instanceId=' + row.instanceId + '&deploymentFileUUID=' + data.deploymentID + '&deploymentName=' + encodeURI(data.resourceName);
this.modelVisible = true
})
},
/** 提交按钮 */
submitForm() {
this.$refs['form'].validate(valid => {
if (valid) {
if (this.form.id != null) {
updateLeave(this.form).then(response => {
this.msgSuccess('修改成功')
this.open = false
this.getList()
})
} else {
addLeave(this.form).then(response => {
this.msgSuccess('新增成功')
this.open = false
this.getList()
})
}
}
})
},
/** 删除按钮操作 */
handleDelete(row) {
const ids = row.id || this.ids
this.$confirm('是否确认删除请假编号为"' + ids + '"的数据项?', '警告', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(function () {
return delLeave(ids)
}).then(() => {
this.getList()
this.msgSuccess('删除成功')
})
},
/** 导出按钮操作 */
handleExport() {
const queryParams = this.queryParams
this.$confirm('是否确认导出所有请假数据项?', '警告', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(function () {
return exportLeave(queryParams)
}).then(response => {
this.download(response.msg)
})
},
chooseMedicine() {
this.form.title = this.createName + "的" + this.form.type + "申请";
}
}
}
</script>

@ -0,0 +1,228 @@
<template>
<div class="app-container">
<el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item label="请假类型" prop="type">
<el-select v-model="queryParams.type" placeholder="请选择请假类型" clearable size="small">
<el-option
v-for="dict in typeOptions"
:key="dict.dictValue"
:label="dict.dictLabel"
:value="dict.dictValue"
/>
</el-select>
</el-form-item>
<el-form-item label="标题" prop="title">
<el-input
v-model="queryParams.title"
placeholder="请输入标题"
clearable
size="small"
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="状态" prop="state">
<el-select v-model="queryParams.state" placeholder="请选择状态" clearable size="small">
<el-option
v-for="dict in stateOptions"
:key="dict.dictValue"
:label="dict.dictLabel"
:value="dict.dictValue"
/>
</el-select>
</el-form-item>
<!-- <el-form-item label="创建者" prop="createBy">-->
<!-- <el-select v-model="queryParams.createBy" placeholder="请选择创建者" clearable size="small">-->
<!-- <el-option label="请选择字典生成" value="" />-->
<!-- </el-select>-->
<!-- </el-form-item>-->
<el-form-item>
<el-button type="cyan" icon="el-icon-search" size="mini" @click="handleQuery"></el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery"></el-button>
</el-form-item>
</el-form>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button
type="warning"
icon="el-icon-download"
size="mini"
@click="handleExport"
v-hasPermi="['workflow:leave:export']"
>导出
</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<el-table v-loading="loading" :data="leaveList">
<el-table-column type="selection" width="55" align="center"/>
<!-- <el-table-column label="主键ID" align="center" prop="id" />-->
<el-table-column label="请假类型" align="center" prop="type" :formatter="typeFormat"/>
<el-table-column label="标题" align="center" prop="title"/>
<el-table-column label="原因" align="center" prop="reason"/>
<el-table-column label="开始时间" align="center" prop="leaveStartTime" width="180">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.leaveStartTime, '{y}-{m}-{d}') }}</span>
</template>
</el-table-column>
<el-table-column label="结束时间" align="center" prop="leaveEndTime" width="180">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.leaveEndTime, '{y}-{m}-{d}') }}</span>
</template>
</el-table-column>
<el-table-column label="创建人" align="center" prop="createName"/>
<el-table-column label="状态" align="center" prop="state" :formatter="stateFormat"/>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template slot-scope="scope">
<el-button
size="mini"
type="text"
icon="el-icon-edit"
@click="historyFory(scope.row)"
v-hasPermi="['workflow:leave:edit']"
>审批详情
</el-button>
</template>
</el-table-column>
</el-table>
<pagination
v-show="total>0"
:total="total"
:page.sync="queryParams.pageNum"
:limit.sync="queryParams.pageSize"
@pagination="getList"
/>
<!-- 查看详细信息话框 -->
<el-dialog :title="title" :visible.sync="open2" width="500px" append-to-body>
<leaveHistoryForm :businessKey="businessKey" v-if="open2"/>
<div slot="footer" class="dialog-footer">
<el-button @click="open2=!open2"></el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import leaveHistoryForm from "./leaveHistoryForm";
import {exportLeave, listLeaveAll} from "@/api/business/workflow/workflow/leave";
export default {
name: 'Leave',
components: {leaveHistoryForm},
data() {
return {
businessKey: '',
//
user: {},
//
loading: true,
//
ids: [],
//
single: true,
//
multiple: true,
//
showSearch: true,
//
total: 0,
//
leaveList: [],
//
title: '',
//
open: false,
open2: false,
//
typeOptions: [],
//
stateOptions: [],
//
queryParams: {
pageNum: 1,
pageSize: 10,
type: null,
title: null,
reason: null,
leaveStartTime: null,
leaveEndTime: null,
instanceId: null,
state: null,
createBy: null
},
}
},
created() {
this.getList()
this.getDicts('activiti_leave_type').then(response => {
this.typeOptions = response.data
})
this.getDicts('activiti_flow_type').then(response => {
this.stateOptions = response.data
})
},
methods: {
/** 查询请假列表 */
getList() {
this.loading = true
listLeaveAll(this.queryParams).then(response => {
this.leaveList = response.rows
this.total = response.total
this.loading = false
})
},
//
typeFormat(row, column) {
return this.selectDictLabel(this.typeOptions, row.type)
},
//
stateFormat(row, column) {
return this.selectDictLabel(this.stateOptions, row.state)
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNum = 1
this.getList()
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm('queryForm')
this.handleQuery()
},
/** 审批详情 */
historyFory(row) {
this.businessKey = row.id
this.open2 = true
this.title = '审批详情'
},
/** 导出按钮操作 */
handleExport() {
const queryParams = this.queryParams
this.$confirm('是否确认导出所有请假数据项?', '警告', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(function () {
return exportLeave(queryParams)
}).then(response => {
this.download(response.msg)
})
},
}
}
</script>

@ -0,0 +1,78 @@
<template>
<div>
<div>
<h2>请假人{{ form.createName }}</h2>
<el-form label-width="80px">
<el-form-item label="请假类型">
<el-input v-model="form.type"/>
</el-form-item>
<el-form-item label="标题">
<el-input v-model="form.title"/>
</el-form-item>
<el-form-item label="原因">
<el-input v-model="form.reason"/>
</el-form-item>
<el-form-item label="开始时间">
<el-input v-model="form.leaveStartTime"/>
</el-form-item>
<el-form-item label="结束时间">
<el-input v-model="form.leaveEndTime"/>
</el-form-item>
</el-form>
</div>
<div v-for="(historyData, index) in fromData"
:key="index">
<h2>{{ historyData.taskNodeName }}</h2>
<h3>审批人:{{ historyData.createName }}</h3>
<h3>审批时间:{{ historyData.createdDate }}</h3>
<el-form v-for="(fistoryFormData, indexH) in historyData.formHistoryDataDTO" :key="indexH" label-width="80px">
<el-form-item :label=fistoryFormData.title>
<el-input v-model="fistoryFormData.value"/>
</el-form-item>
</el-form>
</div>
</div>
</template>
<script>
import {getLeave} from "@/api/business/workflow/workflow/leave";
import {historyFromData} from "@/api/business/workflow/activiti/historyFormdata";
export default {
name: "leaveHistoryForm",
props: {
businessKey: {
type: String
}
},
data() {
return {
//
form: {},
fromData: [],
}
},
created() {
this.getLeave()
this.historyFromData()
},
methods: {
getLeave() {
getLeave(this.businessKey).then(response => {
this.form = response.data
})
},
historyFromData() {
historyFromData(this.businessKey).then(response => {
this.fromData = response.data
})
},
}
}
</script>
<style scoped>
</style>

@ -0,0 +1,3 @@
node_modules/
tmp/
dist/

@ -0,0 +1 @@
package-lock=false

@ -0,0 +1,140 @@
var path = require('path');
module.exports = function(grunt) {
require('load-grunt-tasks')(grunt);
/**
* Resolve external project resource as file path
*/
function resolvePath(project, file) {
return path.join(path.dirname(require.resolve(project)), file);
}
grunt.initConfig({
browserify: {
options: {
browserifyOptions: {
debug: true
},
transform: [
[ 'stringify', {
extensions: [ '.bpmn' ]
} ]
],
plugin: [
'esmify'
]
},
watch: {
options: {
watch: true
},
files: {
'dist/index.js': [ 'app/**/*.js' ]
}
},
app: {
files: {
'dist/index.js': [ 'app/**/*.js' ]
}
}
},
copy: {
diagram_js: {
files: [
{
src: resolvePath('diagram-js', 'assets/diagram-js.css'),
dest: 'dist/css/diagram-js.css'
}
]
},
bpmn_js: {
files: [
{
expand: true,
cwd: resolvePath('bpmn-js', 'dist/assets'),
src: ['**/*.*', '!**/*.js'],
dest: 'dist/vendor'
}
]
},
app: {
files: [
{
expand: true,
cwd: 'app/',
src: ['**/*.*', '!**/*.js'],
dest: 'dist'
}
]
}
},
less: {
options: {
dumpLineNumbers: 'comments',
paths: [
'node_modules'
]
},
styles: {
files: {
'dist/css/app.css': 'styles/app.less'
}
}
},
watch: {
options: {
livereload: true
},
samples: {
files: [ 'app/**/*.*' ],
tasks: [ 'copy:app' ]
},
less: {
files: [
'styles/**/*.less',
'node_modules/bpmn-js-properties-panel/styles/**/*.less'
],
tasks: [
'less'
]
},
},
connect: {
livereload: {
options: {
port: 9013,
livereload: true,
hostname: 'localhost',
open: true,
base: [
'dist'
]
}
}
}
});
// tasks
grunt.registerTask('build', [ 'copy', 'less', 'browserify:app' ]);
grunt.registerTask('auto-build', [
'copy',
'less',
'browserify:watch',
'connect:livereload',
'watch'
]);
grunt.registerTask('default', [ 'build' ]);
};

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

@ -0,0 +1,110 @@
<!DOCTYPE html>
<html >
<head>
<meta charset="utf-8" />
<title>在线绘制流程</title>
<link rel="icon" href="favicon.ico">
<link rel="stylesheet" href="css/diagram-js.css" />
<link rel="stylesheet" href="vendor/bpmn-font/css/bpmn-embedded.css" />
<link rel="stylesheet" href="css/app.css" />
<style>
.item{
display: none;
cursor: pointer;
}
.bjs-powered-by {
display: none;
}
.buttons > li {
/* display: inline-block; */
margin-right: 10px;
height: 26px;
line-height: 26px;
float: left;
}
.buttons > li > a.btn {
background: #00BCD4;
border: none;
outline: none;
padding: 0px 10px;
color: #fff;
display: inline-block;
opacity: 1;
height: 26px;
font-size: 14px;
line-height: 26px;
}
.label {
background: #00BCD4;
border: none;
outline: none;
padding: 0px 10px;
color: #fff;
display: inline-block;
cursor: pointer;
opacity: 1;
height: 26px;
font-size: 14px;
line-height: 26px;
}
.sy-mask{width:100%;
height:100%;
position:fixed;
background:rgba(0,0,0,0.8);
left:0;top:0;z-index:1000;
display: none;
}
.sy-alert{
position:fixed;display:none;background:#fff;border-radius:5px;overflow:hidden;width:300px;max-width:90%;max-height:80%;left:0;right:0;margin:0 auto;z-index:9999}.sy-alert.animated{-webkit-animation-duration:.3s;animation-duration:.3s}.sy-alert .sy-title{height:45px;color:#333;line-height:45px;font-size:15px;border-bottom:1px solid #eee;padding:0 12px}.sy-alert .sy-content{padding:20px;text-align:center;font-size:14px;line-height:24px;color:#666;overflow-y:auto}.sy-alert .sy-btn{height:50%;border-top:1px solid #eee;overflow:hidden}.sy-alert .sy-btn button{float:left;border:0;color:#333;cursor:pointer;background:#fff;width:50%;line-height:45px;font-size:15px;text-align:center}.sy-alert .sy-btn button:nth-child(1){color:#888;border-right:1px solid #eee}.sy-alert.sy-alert-alert .sy-btn button{float:none;width:100%}.sy-alert.sy-alert-tips{text-align:center;width:150px;background:rgba(0,0,0,0.7)}.sy-alert.sy-alert-tips .sy-content{padding:8px;color:#fff;font-size:14px}.sy-alert.sy-alert-model .sy-content{text-align:left}.sy-alert.sy-alert-model .sy-content .form .input-item{margin-bottom:12px;position:relative}.sy-alert.sy-alert-model .sy-content .form .input-item input{display:block;position:relative;width:100%;border:1px solid #eee;padding:10px}.sy-alert.sy-alert-model .sy-content .form .input-item .getcode{border:0;top:0;right:0;position:absolute;background:0;line-height:37px;color:#f60;width:100px;text-align:center}
</style>
</head>
<body>
<div class="content with-diagram" id="js-drop-zone">
<div class="message error">
<div class="note">
<p>无法显示bpms2.0</p>
<div class="details">
<span>错误详细信息</span>
<pre></pre>
</div>
</div>
</div>
<div class="canvas" id="js-canvas"></div>
<div class="properties-panel-parent" id="js-properties-panel"></div>
</div>
<ul class="buttons">
<li class="item upload">
<form id="form1" name="myForm" onsubmit="return false" method="post" enctype="multipart/form-data" title="上传文件">
<input type="file" name="uploadFile" id="uploadFile" accept=".bpmn" style="display: none">
<label class="label" for="uploadFile" >导入</label>
</form>
</li>
<li class="item download">
<a class="btn" href id="downloadBpmn">导出</a>
</li>
<li class="item submit">
<a class="btn" id="js-download-diagram">
部署
</a>
</li>
</ul>
<div class="sy-alert sy-alert-model animated" sy-enter="zoomIn" sy-leave="zoomOut" sy-type="confirm" sy-mask="true" id="alert" >
<div class="sy-title">部署流程</div>
<div class="sy-content">
确认是否部署该流程
<!-- <div class="form">-->
<!-- <p class="input-item"><input id="deploymentName" type="text" placeholder="请输入流程名称"></p>-->
<!-- </div>-->
</div>
<div class="sy-btn">
<button id="sure">确定</button>
<button class="cancel" >取消</button>
</div>
</div>
<div class="sy-mask cancel"></div>
<script src="index.js"></script>
</body>
</html>

@ -0,0 +1,146 @@
import $ from 'jquery';
import BpmnModeler from 'bpmn-js/lib/Modeler';
//import propertiesPanelModule from '../resources/properties-panel';
import propertiesPanelModule from 'bpmn-js-properties-panel';
import propertiesProviderModule from '../resources/properties-panel/provider/activiti';
import activitiModdleDescriptor from '../resources/activiti.json';
import customTranslate from '../resources/customTranslate/customTranslate';
import customControlsModule from '../resources/customControls';
import tools from '../resources/tools'
import diagramXML from '../resources/newDiagram.bpmn';
const proHost = window.location.protocol + "//" + window.location.host;
const href = window.location.href.split("bpmnjs")[0];
const key = href.split(window.location.host)[1];
const publicurl = proHost + key;
// 添加翻译组件
var customTranslateModule = {
translate: ['value', customTranslate]
};
var container = $('#js-drop-zone');
var canvas = $('#js-canvas');
var bpmnModeler = new BpmnModeler({
container: canvas,
propertiesPanel: {
parent: '#js-properties-panel'
},
additionalModules: [
propertiesPanelModule,
propertiesProviderModule,
customControlsModule,
customTranslateModule
],
moddleExtensions: {
activiti:activitiModdleDescriptor
}
});
container.removeClass('with-diagram');
// 判断浏览器支持程度
if (!window.FileList || !window.FileReader) {
window.alert('请使用谷歌、火狐、IE10+浏览器');
} else {
tools.registerFileDrop(container, tools.createDiagram(diagramXML, bpmnModeler, container));
}
$(function () {
// 创建bpmn
var param = tools.getUrlParam(window.location.href)
$('.item').show()
if (param.type === 'addBpmn') {
tools.createDiagram(diagramXML, bpmnModeler, container);
} else if (param.type === 'lookBpmn') { //编辑bpmn
$('.item').hide()
$('.download').show()
const Id = param.deploymentFileUUID || '6d4af2dc-bab0-11ea-b584-3cf011eaafca'
const Name=param.deploymentName || 'String.bpmn'
const instanceId=param.instanceId
var param={
"deploymentId":Id,
"resourceName":decodeURI(Name)
}
if(instanceId){
var param1={
instanceId
}
$.ajax({
url: localStorage.getItem("VUE_APP_BASE_API")+'/activitiHistory/gethighLine',
// url: 'http://localhost:8080/activitiHistory/gethighLine',
type: 'GET',
data: param1,
dataType:'json',
success: function (result) {
console.log(result)
var ColorJson=tools.getByColor(result.data)
$.ajax({
url: localStorage.getItem("VUE_APP_BASE_API")+'/processDefinition/getDefinitionXML',
// url: 'http://localhost:8080/processDefinition/getDefinitionXML',
type: 'GET',
data: param,
dataType:'text',
success: function (result) {
var newXmlData = result
tools.createDiagram(newXmlData, bpmnModeler, container);
setTimeout(function () {
for (var i in ColorJson) {
tools.setColor(ColorJson[i],bpmnModeler)
}
}, 200)
},
error: function (err) {
console.log(err)
}
});
},
error: function (err) {
console.log(err)
}
});
}else{
//加载后台方法获取xml
$.ajax({
url: localStorage.getItem("VUE_APP_BASE_API")+'/processDefinition/getDefinitionXML',
// url: 'http://localhost:8080/processDefinition/getDefinitionXML',
type: 'GET',
data: param,
dataType:'text',
success: function (result) {
var newXmlData = result
tools.createDiagram(newXmlData, bpmnModeler, container);
},
error: function (err) {
console.log(err)
}
});
}
} else if(param.type === "historyBpmn") { // bpmn历史
$('.item').hide()
$('.download').show()
}
// 点击新增
$('#js-download-diagram').on("click", function () {
tools.syopen('alert')
})
// 点击取消
$('.cancel').on("click",function () {
tools.syhide('alert')
})
// 点击确定
$('#sure').on('click',function(){
// const text=$("#deploymentName").val()
tools.saveBpmn(bpmnModeler)
})
// 点击下载
$("#downloadBpmn").on("click", function () {
tools.downLoad(bpmnModeler)
})
// 点击上传
$("#uploadFile").on("change", function () {
tools.upload(bpmnModeler,container)
})
});

@ -0,0 +1,48 @@
{
"name": "bpmn-js-example-properties-panel",
"version": "0.0.0",
"description": "A bpmn-js modeler + properties panel example",
"main": "app/index.js",
"scripts": {
"all": "grunt",
"dev": "grunt auto-build"
},
"repository": {
"type": "git",
"url": "https://github.com/bpmn-io/bpmn-js-examples"
},
"keywords": [
"bpmnjs-example"
],
"author": {
"name": "Nico Rehwaldt",
"url": "https://github.com/nikku"
},
"contributors": [
{
"name": "bpmn.io contributors",
"url": "https://github.com/bpmn-io"
}
],
"license": "MIT",
"devDependencies": {
"esmify": "^2.1.1",
"grunt": "^1.0.4",
"grunt-browserify": "^5.3.0",
"grunt-contrib-connect": "^2.0.0",
"grunt-contrib-copy": "^1.0.0",
"grunt-contrib-less": "^2.0.0",
"grunt-contrib-watch": "^1.1.0",
"load-grunt-tasks": "^5.0.0",
"stringify": "^5.2.0"
},
"dependencies": {
"bpmn-js": "^7.0.0",
"bpmn-js-properties-panel": "^0.32.0",
"camunda-bpmn-moddle": "^4.0.1",
"diagram-js": "^5.0.0",
"jquery": "^3.4.1",
"min-dash": "^3.5.0",
"x2js": "^3.4.0"
}
}

File diff suppressed because it is too large Load Diff

@ -0,0 +1,76 @@
export default class CustomContextPad {
constructor(config, contextPad, create, elementFactory, injector, translate) {
this.create = create;
this.elementFactory = elementFactory;
this.translate = translate;
//自动摆放位置
if (config.autoPlace !== false) {
this.autoPlace = injector.get('autoPlace', false);
}
//注册工具
contextPad.registerProvider(this);
}
getContextPadEntries(element) {
const {
autoPlace,
create,
elementFactory,
translate
} = this;
function appendUserTask(event, element) {
if (autoPlace) {
const shape = elementFactory.createShape({ type: 'bpmn:UserTask' });
autoPlace.append(element, shape);
} else {
appendUserTaskStart(event, element);
}
}
function appendUserTaskStart(event) {
const shape = elementFactory.createShape({ type: 'bpmn:UserTask' });
create.start(event, shape, element);
}
function appendCallActivityStart(event) {
const shape = elementFactory.createShape({ type: 'bpmn:CallActivity' });
create.start(event, shape, element);
}
function appendCallActivity(event, element) {
if (autoPlace) {
const shape = elementFactory.createShape({ type: 'bpmn:CallActivity' });
autoPlace.append(element, shape);
} else {
appendCallActivityStart(event, element);
}
}
return {
'append.user-task': {
group: 'model',
className: 'bpmn-icon-user-task',
title: translate('Append ServiceTask'),
action: {
click: appendUserTask,
dragstart: appendUserTaskStart
}
},
'append.call-activity':{
group: 'model',
className: 'bpmn-icon-call-activity',
title: translate('Append CallActivity'),
action: {
click: appendCallActivity,
dragstart: appendCallActivityStart
}
}
};
}
}
CustomContextPad.$inject = [
'config',
'contextPad',
'create',
'elementFactory',
'injector',
'translate'
];

@ -0,0 +1,54 @@
export default class CustomPalette {
constructor(create, elementFactory, palette, translate) {
this.create = create;
this.elementFactory = elementFactory;
this.translate = translate;
palette.registerProvider(this);
}
getPaletteEntries(element) {
const {
create,
elementFactory,
translate
} = this;
function createServiceTask(event) {
const shape = elementFactory.createShape({ type: 'bpmn:UserTask' });
create.start(event, shape);
}
function createCallActivity(event) {
const shape = elementFactory.createShape({ type: 'bpmn:CallActivity' });
create.start(event, shape);
}
return {
'create.user-task': {
group: 'activity',
className: 'bpmn-icon-user-task',
title: translate('Create UserTask'),
action: {
dragstart: createServiceTask,
click: createServiceTask
}
},
'create.call-activity': {
group: 'activity',
className: 'bpmn-icon-call-activity',
title: translate('Create CallActivity'),
action: {
dragstart: createCallActivity,
click: createCallActivity
}
}
}
}
}
CustomPalette.$inject = [
'create',
'elementFactory',
'palette',
'translate'
];

@ -0,0 +1,8 @@
import CustomContextPad from './CustomContextPad';
import CustomPalette from './CustomPalette';
export default {
__init__: [ 'customContextPad', 'customPalette' ],
customContextPad: [ 'type', CustomContextPad ],
customPalette: [ 'type', CustomPalette ]
};

@ -0,0 +1,12 @@
import translations from './translationsGerman';
export default function customTranslate(template, replacements) {
replacements = replacements || {};
template = translations[template] || template;
return template.replace(/{([^}]+)}/g, function(_, key) {
var str = replacements[key];
if(translations[replacements[key]] != null && translations [replacements[key]] != 'undefined'){
str = translations[replacements[key]];
}
return str || '{' + key + '}';
});
}

@ -0,0 +1,239 @@
export default {
// Labels
'Activate the global connect tool' : '激活全局连接工具',
'Append {type}': '追加 {type}',
'Append EndEvent': '追加 结束事件 ',
'Append Task':'追加 任务',
'Append Gateway':'追加 网关',
'Append Intermediate/Boundary Event':'追加 中间/边界 事件',
'Add Lane above': '在上面添加道',
'Divide into two Lanes': '分割成两个道',
'Divide into three Lanes': '分割成三个道',
'Add Lane below': '在下面添加道',
'Append compensation activity': '追加补偿活动',
'Change type': '修改类型',
'Connect using Association': '使用关联连接',
'Connect using Sequence/MessageFlow or Association': '使用顺序/消息流或者关联连接',
'Connect using DataInputAssociation': '使用数据输入关联连接',
'Remove': '移除',
'Activate the hand tool': '激活抓手工具',
'Activate the lasso tool': '激活套索工具',
'Activate the create/remove space tool': '激活创建/删除空间工具',
'Create expanded SubProcess': '创建扩展子过程',
'Create IntermediateThrowEvent/BoundaryEvent' : '创建中间抛出事件/边界事件',
'Create Pool/Participant': '创建池/参与者',
'Parallel Multi Instance': '并行多重事件',
'Sequential Multi Instance': '时序多重事件',
'DataObjectReference':'数据对象参考',
'DataStoreReference':'数据存储参考',
'Loop': '循环',
'Ad-hoc': '即席',
'Create {type}': '创建 {type}',
'Create Task':'创建任务',
'Create StartEvent':'创建开始事件',
'Create EndEvent':'创建结束事件',
'Create Group':'创建组',
'Task': '任务',
'Send Task': '发送任务',
'Receive Task': '接收任务',
'User Task': '用户任务',
'Manual Task': '手工任务',
'Business Rule Task': '业务规则任务',
'Service Task': '服务任务',
'Script Task': '脚本任务',
'Call Activity': '调用活动',
'Sub Process (collapsed)': '子流程(折叠的)',
'Sub Process (expanded)': '子流程(展开的)',
'Start Event': '开始事件',
'StartEvent': '开始事件',
'Intermediate Throw Event': '中间事件',
'End Event': '结束事件',
'EndEvent': '结束事件',
'Create Gateway': '创建网关',
'GateWay':'网关',
'Create Intermediate/Boundary Event': '创建中间/边界事件',
'Message Start Event': '消息开始事件',
'Timer Start Event': '定时开始事件',
'Conditional Start Event': '条件开始事件',
'Signal Start Event': '信号开始事件',
'Error Start Event': '错误开始事件',
'Escalation Start Event': '升级开始事件',
'Compensation Start Event': '补偿开始事件',
'Message Start Event (non-interrupting)': '消息开始事件(非中断)',
'Timer Start Event (non-interrupting)': '定时开始事件(非中断)',
'Conditional Start Event (non-interrupting)': '条件开始事件(非中断)',
'Signal Start Event (non-interrupting)': '信号开始事件(非中断)',
'Escalation Start Event (non-interrupting)': '升级开始事件(非中断)',
'Message Intermediate Catch Event': '消息中间捕获事件',
'Message Intermediate Throw Event': '消息中间抛出事件',
'Timer Intermediate Catch Event': '定时中间捕获事件',
'Escalation Intermediate Throw Event': '升级中间抛出事件',
'Conditional Intermediate Catch Event': '条件中间捕获事件',
'Link Intermediate Catch Event': '链接中间捕获事件',
'Link Intermediate Throw Event': '链接中间抛出事件',
'Compensation Intermediate Throw Event': '补偿中间抛出事件',
'Signal Intermediate Catch Event': '信号中间捕获事件',
'Signal Intermediate Throw Event': '信号中间抛出事件',
'Message End Event': '消息结束事件',
'Escalation End Event': '定时结束事件',
'Error End Event': '错误结束事件',
'Cancel End Event': '取消结束事件',
'Compensation End Event': '补偿结束事件',
'Signal End Event': '信号结束事件',
'Terminate End Event': '终止结束事件',
'Message Boundary Event': '消息边界事件',
'Message Boundary Event (non-interrupting)': '消息边界事件(非中断)',
'Timer Boundary Event': '定时边界事件',
'Timer Boundary Event (non-interrupting)': '定时边界事件(非中断)',
'Escalation Boundary Event': '升级边界事件',
'Escalation Boundary Event (non-interrupting)': '升级边界事件(非中断)',
'Conditional Boundary Event': '条件边界事件',
'Conditional Boundary Event (non-interrupting)': '条件边界事件(非中断)',
'Error Boundary Event': '错误边界事件',
'Cancel Boundary Event': '取消边界事件',
'Signal Boundary Event': '信号边界事件',
'Signal Boundary Event (non-interrupting)': '信号边界事件(非中断)',
'Compensation Boundary Event': '补偿边界事件',
'Exclusive Gateway': '互斥网关',
'Parallel Gateway': '并行网关',
'Inclusive Gateway': '相容网关',
'Complex Gateway': '复杂网关',
'Event based Gateway': '事件网关',
'Transaction': '转运',
'Sub Process': '子流程',
'Event Sub Process': '事件子流程',
'Collapsed Pool': '折叠池',
'Expanded Pool': '展开池',
// Errors
'no parent for {element} in {parent}': '在{parent}里,{element}没有父类',
'no shape type specified': '没有指定的形状类型',
'flow elements must be children of pools/participants': '流元素必须是池/参与者的子类',
'out of bounds release': 'out of bounds release',
'more than {count} child lanes': '子道大于{count} ',
'element required': '元素不能为空',
'diagram not part of bpmn:Definitions': '流程图不符合bpmn规范',
'no diagram to display': '没有可展示的流程图',
'no process or collaboration to display': '没有可展示的流程/协作',
'element {element} referenced by {referenced}#{property} not yet drawn': '由{referenced}#{property}引用的{element}元素仍未绘制',
'already rendered {element}': '{element} 已被渲染',
'failed to import {element}': '导入{element}失败',
//属性面板的参数
'Id':'编号',
'Name':'名称',
'General':'常规',
'Details':'详情',
'Message Name':'消息名称',
'Message':'消息',
'Initiator':'创建者',
'Asynchronous Continuations':'持续异步',
'Asynchronous Before':'异步前',
'Asynchronous After':'异步后',
'Job Configuration':'工作配置',
'Exclusive':'排除',
'Job Priority':'工作优先级',
'Retry Time Cycle':'重试时间周期',
'Documentation':'文档',
'Element Documentation':'元素文档',
'History Configuration':'历史配置',
'History Time To Live':'历史的生存时间',
'Forms':'表单',
'Form Key':'表单key',
'Form Fields':'表单字段',
'Business Key':'业务key',
'Form Field':'表单字段',
'ID':'编号',
'Type':'类型',
'Label':'名称',
'Default Value':'默认值',
'Validation':'校验',
'Add Constraint':'添加约束',
'Config':'配置',
'Properties':'属性',
'Add Property':'添加属性',
'Value':'值',
'Add':'添加',
'Values':'值',
'Add Value':'添加值',
'Listeners':'监听器',
'Execution Listener':'执行监听',
'Event Type':'事件类型',
'Listener Type':'监听器类型',
'Java Class':'Java类',
'Expression':'表达式',
'Must provide a value':'必须提供一个值',
'Delegate Expression':'代理表达式',
'Script':'脚本',
'Script Format':'脚本格式',
'Script Type':'脚本类型',
'Inline Script':'内联脚本',
'External Script':'外部脚本',
'Resource':'资源',
'Field Injection':'字段注入',
'Extensions':'扩展',
'Input/Output':'输入/输出',
'Input Parameters':'输入参数',
'Output Parameters':'输出参数',
'Parameters':'参数',
'Output Parameter':'输出参数',
'Timer Definition Type':'定时器定义类型',
'Timer Definition':'定时器定义',
'Date':'日期',
'Duration':'持续',
'Cycle':'循环',
'Signal':'信号',
'Signal Name':'信号名称',
'Escalation':'升级',
'Error':'错误',
'Link Name':'链接名称',
'Condition':'条件名称',
'Variable Name':'变量名称',
'Variable Event':'变量事件',
'Specify more than one variable change event as a comma separated list.':'多个变量事件以逗号隔开',
'Wait for Completion':'等待完成',
'Activity Ref':'活动参考',
'Version Tag':'版本标签',
'Executable':'可执行文件',
'External Task Configuration':'扩展任务配置',
'Task Priority':'任务优先级',
'External':'外部',
'Connector':'连接器',
'Must configure Connector':'必须配置连接器',
'Connector Id':'连接器编号',
'Implementation':'实现方式',
'Field Injections':'字段注入',
'Fields':'字段',
'Result Variable':'结果变量',
'Topic':'主题',
'Configure Connector':'配置连接器',
'Input Parameter':'输入参数',
'Assignee':'代理人',
'Candidate Users':'候选用户',
'Candidate Groups':'候选组',
'Due Date':'到期时间',
'Follow Up Date':'跟踪日期',
'Priority':'优先级',
'The follow up date as an EL expression (e.g. ${someDate} or an ISO date (e.g. 2015-06-26T09:54:00)':'跟踪日期必须符合EL表达式 ${someDate} ,或者一个ISO标准日期2015-06-26T09:54:00',
'The due date as an EL expression (e.g. ${someDate} or an ISO date (e.g. 2015-06-26T09:54:00)':'跟踪日期必须符合EL表达式 ${someDate} ,或者一个ISO标准日期2015-06-26T09:54:00',
'Variables':'变量',
'Candidate Starter Configuration':'候选开始配置',
'Task Listener':'任务监听器',
'Candidate Starter Groups':'候选开始组',
'Candidate Starter Users':'候选开始用户',
'Tasklist Configuration':'任务列表配置',
'Startable':'启动',
'Specify more than one group as a comma separated list.':'指定多个组,用逗号分隔',
'Specify more than one user as a comma separated list.':'指定多个用户,用逗号分隔',
'This maps to the process definition key.':'这会映射为流程定义的键',
'CallActivity Type':'调用活动类型',
'Condition Type':'条件类型',
'Create UserTask':'创建用户任务',
'Create CallActivity':'创建调用活动',
'Called Element':'调用元素',
'Create DataObjectReference':'创建数据对象引用',
'Create DataStoreReference':'创建数据存储引用',
'Multi Instance':'多实例',
'Loop Cardinality':'实例数量',
'Collection':'任务参与人列表',
'Element Variable':'元素变量',
'Completion Condition':'完成条件'
};

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<bpmn2:definitions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:bpmn2="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" xsi:schemaLocation="http://www.omg.org/spec/BPMN/20100524/MODEL BPMN20.xsd" id="sample-diagram" targetNamespace="http://activiti.org/bpmn">
<bpmn2:process id="Process_1" isExecutable="true">
<bpmn2:startEvent id="StartEvent_1"/>
</bpmn2:process>
<bpmndi:BPMNDiagram id="BPMNDiagram_1">
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Process_1">
<bpmndi:BPMNShape id="_BPMNShape_StartEvent_2" bpmnElement="StartEvent_1">
<dc:Bounds height="36.0" width="36.0" x="412.0" y="240.0"/>
</bpmndi:BPMNShape>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</bpmn2:definitions>

@ -0,0 +1,67 @@
'use strict';
var DEFAULT_PRIORITY = 1000;
/**
* A component that decides upon the visibility / editable
* state of properties in the properties panel.
*
* Implementors must subclass this component and override
* {@link PropertiesActivator#isEntryVisible} and
* {@link PropertiesActivator#isPropertyEditable} to provide
* custom behavior.
*
* @class
* @constructor
*
* @param {EventBus} eventBus
* @param {Number} [priority] at which priority to hook into the activation
*/
function PropertiesActivator(eventBus, priority) {
var self = this;
priority = priority || DEFAULT_PRIORITY;
eventBus.on('propertiesPanel.isEntryVisible', priority, function(e) {
return self.isEntryVisible(e.entry, e.element);
});
eventBus.on('propertiesPanel.isPropertyEditable', priority, function(e) {
return self.isPropertyEditable(e.entry, e.propertyName, e.element);
});
}
PropertiesActivator.$inject = [ 'eventBus' ];
module.exports = PropertiesActivator;
/**
* Should the given entry be visible for the specified element.
*
* @method PropertiesActivator#isEntryVisible
*
* @param {EntryDescriptor} entry
* @param {ModdleElement} element
*
* @returns {Boolean}
*/
PropertiesActivator.prototype.isEntryVisible = function(entry, element) {
return true;
};
/**
* Should the given property be editable for the specified element
*
* @method PropertiesActivator#isPropertyEditable
*
* @param {EntryDescriptor} entry
* @param {String} propertyName
* @param {ModdleElement} element
*
* @returns {Boolean}
*/
PropertiesActivator.prototype.isPropertyEditable = function(entry, propertyName, element) {
return true;
};

@ -0,0 +1,252 @@
'use strict';
var domQuery = require('min-dom').query,
domClear = require('min-dom').clear,
is = require('bpmn-js/lib/util/ModelUtil').is,
forEach = require('lodash/forEach'),
domify = require('min-dom').domify,
Ids = require('ids').default;
var SPACE_REGEX = /\s/;
// for QName validation as per http://www.w3.org/TR/REC-xml/#NT-NameChar
var QNAME_REGEX = /^([a-z][\w-.]*:)?[a-z_][\w-.]*$/i;
// for ID validation as per BPMN Schema (QName - Namespace)
var ID_REGEX = /^[a-z_][\w-.]*$/i;
var PLACEHOLDER_REGEX = /\$\{([^}]*)\}/g;
var HTML_ESCAPE_MAP = {
'&': '&amp;',
'<': '&lt;',
'>': '&gt;',
'"': '&quot;',
'\'': '&#39;'
};
function selectedOption(selectBox) {
if (selectBox.selectedIndex >= 0) {
return selectBox.options[selectBox.selectedIndex].value;
}
}
module.exports.selectedOption = selectedOption;
function selectedType(elementSyntax, inputNode) {
var typeSelect = domQuery(elementSyntax, inputNode);
return selectedOption(typeSelect);
}
module.exports.selectedType = selectedType;
/**
* Retrieve the root element the document this
* business object is contained in.
*
* @return {ModdleElement}
*/
function getRoot(businessObject) {
var parent = businessObject;
while (parent.$parent) {
parent = parent.$parent;
}
return parent;
}
module.exports.getRoot = getRoot;
/**
* filters all elements in the list which have a given type.
* removes a new list
*/
function filterElementsByType(objectList, type) {
var list = objectList || [];
var result = [];
forEach(list, function(obj) {
if (is(obj, type)) {
result.push(obj);
}
});
return result;
}
module.exports.filterElementsByType = filterElementsByType;
function findRootElementsByType(businessObject, referencedType) {
var root = getRoot(businessObject);
return filterElementsByType(root.rootElements, referencedType);
}
module.exports.findRootElementsByType = findRootElementsByType;
function removeAllChildren(domElement) {
while (domElement.firstChild) {
domElement.removeChild(domElement.firstChild);
}
}
module.exports.removeAllChildren = removeAllChildren;
/**
* adds an empty option to the list
*/
function addEmptyParameter(list) {
return list.push({ 'label': '', 'value': '', 'name': '' });
}
module.exports.addEmptyParameter = addEmptyParameter;
/**
* returns a list with all root elements for the given parameter 'referencedType'
*/
function refreshOptionsModel(businessObject, referencedType) {
var model = [];
var referableObjects = findRootElementsByType(businessObject, referencedType);
forEach(referableObjects, function(obj) {
model.push({
label: (obj.name || '') + ' (id='+obj.id+')',
value: obj.id,
name: obj.name
});
});
return model;
}
module.exports.refreshOptionsModel = refreshOptionsModel;
/**
* fills the drop down with options
*/
function updateOptionsDropDown(domSelector, businessObject, referencedType, entryNode) {
var options = refreshOptionsModel(businessObject, referencedType);
addEmptyParameter(options);
var selectBox = domQuery(domSelector, entryNode);
domClear(selectBox);
forEach(options, function(option) {
var optionEntry = domify('<option value="' + escapeHTML(option.value) + '">' + escapeHTML(option.label) + '</option>');
selectBox.appendChild(optionEntry);
});
return options;
}
module.exports.updateOptionsDropDown = updateOptionsDropDown;
/**
* checks whether the id value is valid
*
* @param {ModdleElement} bo
* @param {String} idValue
* @param {Function} translate
*
* @return {String} error message
*/
function isIdValid(bo, idValue, translate) {
var assigned = bo.$model.ids.assigned(idValue);
var idExists = assigned && assigned !== bo;
if (!idValue || idExists) {
return translate('Element must have an unique id.');
}
return validateId(idValue, translate);
}
module.exports.isIdValid = isIdValid;
function validateId(idValue, translate) {
idValue = stripPlaceholders(idValue);
if (containsSpace(idValue)) {
return translate('Id must not contain spaces.');
}
if (!ID_REGEX.test(idValue)) {
if (QNAME_REGEX.test(idValue)) {
return translate('Id must not contain prefix.');
}
return translate('Id must be a valid QName.');
}
}
module.exports.validateId = validateId;
function containsSpace(value) {
return SPACE_REGEX.test(value);
}
module.exports.containsSpace = containsSpace;
function stripPlaceholders(idValue) {
// replace expression e.g. ${VERSION_TAG}
// use only the content between ${}
// for the REGEX check
return idValue.replace(PLACEHOLDER_REGEX, '$1');
}
/**
* generate a semantic id with given prefix
*/
function nextId(prefix) {
var ids = new Ids([32,32,1]);
return ids.nextPrefixed(prefix);
}
module.exports.nextId = nextId;
function triggerClickEvent(element) {
var evt;
var eventType = 'click';
if (document.createEvent) {
try {
// Chrome, Safari, Firefox
evt = new MouseEvent((eventType), { view: window, bubbles: true, cancelable: true });
} catch (e) {
// IE 11, PhantomJS (wat!)
evt = document.createEvent('MouseEvent');
evt.initEvent((eventType), true, true);
}
return element.dispatchEvent(evt);
} else {
// Welcome IE
evt = document.createEventObject();
return element.fireEvent('on' + eventType, evt);
}
}
module.exports.triggerClickEvent = triggerClickEvent;
function escapeHTML(str) {
str = '' + str;
return str && str.replace(/[&<>"']/g, function(match) {
return HTML_ESCAPE_MAP[match];
});
}
module.exports.escapeHTML = escapeHTML;

@ -0,0 +1,101 @@
'use strict';
var elementHelper = require('../helper/ElementHelper');
/**
* A handler capable of creating a new element under a provided parent
* and updating / creating a reference to it in one atomic action.
*
* @class
* @constructor
*/
function CreateAndReferenceElementHandler(elementRegistry, bpmnFactory) {
this._elementRegistry = elementRegistry;
this._bpmnFactory = bpmnFactory;
}
CreateAndReferenceElementHandler.$inject = [ 'elementRegistry', 'bpmnFactory' ];
module.exports = CreateAndReferenceElementHandler;
// api ////////////////////
/**
* Creates a new element under a provided parent and updates / creates a reference to it in
* one atomic action.
*
* @method CreateAndReferenceElementHandler#execute
*
* @param {Object} context
* @param {djs.model.Base} context.element which is the context for the reference
* @param {moddle.referencingObject} context.referencingObject the object which creates the reference
* @param {String} context.referenceProperty the property of the referencingObject which makes the reference
* @param {moddle.newObject} context.newObject the new object to add
* @param {moddle.newObjectContainer} context.newObjectContainer the container for the new object
*
* @returns {Array<djs.mode.Base>} the updated element
*/
CreateAndReferenceElementHandler.prototype.execute = function(context) {
var referencingObject = ensureNotNull(context.referencingObject, 'referencingObject'),
referenceProperty = ensureNotNull(context.referenceProperty, 'referenceProperty'),
newObject = ensureNotNull(context.newObject, 'newObject'),
newObjectContainer = ensureNotNull(context.newObjectContainer, 'newObjectContainer'),
newObjectParent = ensureNotNull(context.newObjectParent, 'newObjectParent'),
changed = [ context.element ]; // this will not change any diagram-js elements
// create new object
var referencedObject = elementHelper
.createElement(newObject.type, newObject.properties, newObjectParent, this._bpmnFactory);
context.referencedObject = referencedObject;
// add to containing list
newObjectContainer.push(referencedObject);
// adjust reference attribute
context.previousReference = referencingObject[referenceProperty];
referencingObject[referenceProperty] = referencedObject;
context.changed = changed;
// indicate changed on objects affected by the update
return changed;
};
/**
* Reverts the update
*
* @method CreateAndReferenceElementHandler#revert
*
* @param {Object} context
*
* @returns {djs.mode.Base} the updated element
*/
CreateAndReferenceElementHandler.prototype.revert = function(context) {
var referencingObject = context.referencingObject,
referenceProperty = context.referenceProperty,
previousReference = context.previousReference,
referencedObject = context.referencedObject,
newObjectContainer = context.newObjectContainer;
// reset reference
referencingObject.set(referenceProperty, previousReference);
// remove new element
newObjectContainer.splice(newObjectContainer.indexOf(referencedObject), 1);
return context.changed;
};
// helpers //////////////
function ensureNotNull(prop, name) {
if (!prop) {
throw new Error(name + ' required');
}
return prop;
}

@ -0,0 +1,110 @@
'use strict';
var forEach = require('lodash/forEach');
var elementHelper = require('../helper/ElementHelper');
/**
* A handler that implements a BPMN 2.0 property update
* for business objects which are not represented in the
* diagram.
*
* This is useful in the context of the properties panel in
* order to update child elements of elements visible in
* the diagram.
*
* Example: perform an update of a specific event definition
* of an intermediate event.
*
* @class
* @constructor
*/
function CreateBusinessObjectListHandler(elementRegistry, bpmnFactory) {
this._elementRegistry = elementRegistry;
this._bpmnFactory = bpmnFactory;
}
CreateBusinessObjectListHandler.$inject = [ 'elementRegistry', 'bpmnFactory' ];
module.exports = CreateBusinessObjectListHandler;
function ensureNotNull(prop, name) {
if (!prop) {
throw new Error(name + ' required');
}
return prop;
}
function ensureList(prop, name) {
if (!prop || Object.prototype.toString.call(prop) !== '[object Array]') {
throw new Error(name + ' needs to be a list');
}
return prop;
}
// api /////////////////////////////////////////////
/**
* Creates a new element under a provided parent and updates / creates a reference to it in
* one atomic action.
*
* @method CreateBusinessObjectListHandler#execute
*
* @param {Object} context
* @param {djs.model.Base} context.element which is the context for the reference
* @param {moddle.referencingObject} context.referencingObject the object which creates the reference
* @param {String} context.referenceProperty the property of the referencingObject which makes the reference
* @param {moddle.newObject} context.newObject the new object to add
* @param {moddle.newObjectContainer} context.newObjectContainer the container for the new object
*
* @return {Array<djs.mode.Base>} the updated element
*/
CreateBusinessObjectListHandler.prototype.execute = function(context) {
var currentObject = ensureNotNull(context.currentObject, 'currentObject'),
propertyName = ensureNotNull(context.propertyName, 'propertyName'),
newObjects = ensureList(context.newObjects, 'newObjects'),
changed = [ context.element ]; // this will not change any diagram-js elements
var childObjects = [];
var self = this;
// create new array of business objects
forEach(newObjects, function(obj) {
var element = elementHelper.createElement(obj.type, obj.properties, currentObject, self._bpmnFactory);
childObjects.push(element);
});
context.childObject = childObjects;
// adjust array reference in the parent business object
context.previousChilds = currentObject[propertyName];
currentObject[propertyName] = childObjects;
context.changed = changed;
// indicate changed on objects affected by the update
return changed;
};
/**
* Reverts the update
*
* @method CreateBusinessObjectListHandler#revert
*
* @param {Object} context
*
* @return {djs.mode.Base} the updated element
*/
CreateBusinessObjectListHandler.prototype.revert = function(context) {
var currentObject = context.currentObject,
propertyName = context.propertyName,
previousChilds = context.previousChilds;
// remove new element
currentObject.set(propertyName, previousChilds);
return context.changed;
};

@ -0,0 +1,32 @@
'use strict';
var forEach = require('lodash/forEach');
/**
* A handler that combines and executes multiple commands.
*
* All updates are bundled on the command stack and executed in one step.
* This also makes it possible to revert the changes in one step.
*
* Example use case: remove the camunda:formKey attribute and in addition
* add all form fields needed for the camunda:formData property.
*
* @class
* @constructor
*/
function MultiCommandHandler(commandStack) {
this._commandStack = commandStack;
}
MultiCommandHandler.$inject = [ 'commandStack' ];
module.exports = MultiCommandHandler;
MultiCommandHandler.prototype.preExecute = function(context) {
var commandStack = this._commandStack;
forEach(context, function(command) {
commandStack.execute(command.cmd, command.context);
});
};

@ -0,0 +1,131 @@
'use strict';
var reduce = require('lodash/transform'),
is = require('bpmn-js/lib/util/ModelUtil').is,
keys = require('lodash/keys'),
forEach = require('lodash/forEach');
/**
* A handler that implements a BPMN 2.0 property update
* for business objects which are not represented in the
* diagram.
*
* This is useful in the context of the properties panel in
* order to update child elements of elements visible in
* the diagram.
*
* Example: perform an update of a specific event definition
* of an intermediate event.
*
* @class
* @constructor
*/
function UpdateBusinessObjectHandler(elementRegistry) {
this._elementRegistry = elementRegistry;
}
UpdateBusinessObjectHandler.$inject = [ 'elementRegistry' ];
module.exports = UpdateBusinessObjectHandler;
/**
* returns the root element
*/
function getRoot(businessObject) {
var parent = businessObject;
while (parent.$parent) {
parent = parent.$parent;
}
return parent;
}
function getProperties(businessObject, propertyNames) {
return reduce(propertyNames, function(result, key) {
result[key] = businessObject.get(key);
return result;
}, {});
}
function setProperties(businessObject, properties) {
forEach(properties, function(value, key) {
businessObject.set(key, value);
});
}
// api /////////////////////////////////////////////
/**
* Updates a business object with a list of new properties
*
* @method UpdateBusinessObjectHandler#execute
*
* @param {Object} context
* @param {djs.model.Base} context.element the element which has a child business object updated
* @param {moddle.businessObject} context.businessObject the businessObject to update
* @param {Object} context.properties a list of properties to set on the businessObject
*
* @return {Array<djs.mode.Base>} the updated element
*/
UpdateBusinessObjectHandler.prototype.execute = function(context) {
var element = context.element,
businessObject = context.businessObject,
rootElements = getRoot(businessObject).rootElements,
referenceType = context.referenceType,
referenceProperty = context.referenceProperty,
changed = [ element ]; // this will not change any diagram-js elements
if (!element) {
throw new Error('element required');
}
if (!businessObject) {
throw new Error('businessObject required');
}
var properties = context.properties,
oldProperties = context.oldProperties || getProperties(businessObject, keys(properties));
// check if there the update needs an external element for reference
if (typeof referenceType !== 'undefined' && typeof referenceProperty !== 'undefined') {
forEach(rootElements, function(rootElement) {
if (is(rootElement, referenceType)) {
if (rootElement.id === properties[referenceProperty]) {
properties[referenceProperty] = rootElement;
}
}
});
}
// update properties
setProperties(businessObject, properties);
// store old values
context.oldProperties = oldProperties;
context.changed = changed;
// indicate changed on objects affected by the update
return changed;
};
/**
* Reverts the update
*
* @method UpdateBusinessObjectHandler#revert
*
* @param {Object} context
*
* @return {djs.mode.Base} the updated element
*/
UpdateBusinessObjectHandler.prototype.revert = function(context) {
var oldProperties = context.oldProperties,
businessObject = context.businessObject;
// update properties
setProperties(businessObject, oldProperties);
return context.changed;
};

@ -0,0 +1,115 @@
'use strict';
var forEach = require('lodash/forEach');
/**
* A handler that implements a BPMN 2.0 property update
* for business object lists which are not represented in the
* diagram.
*
* This is useful in the context of the properties panel in
* order to update child elements of elements visible in
* the diagram.
*
* Example: perform an update of a specific event definition
* of an intermediate event.
*
* @class
* @constructor
*/
function UpdateBusinessObjectListHandler(elementRegistry, bpmnFactory) {
this._elementRegistry = elementRegistry;
this._bpmnFactory = bpmnFactory;
}
UpdateBusinessObjectListHandler.$inject = [ 'elementRegistry', 'bpmnFactory' ];
module.exports = UpdateBusinessObjectListHandler;
function ensureNotNull(prop, name) {
if (!prop) {
throw new Error(name + 'required');
}
return prop;
}
// api /////////////////////////////////////////////
/**
* Updates a element under a provided parent.
*/
UpdateBusinessObjectListHandler.prototype.execute = function(context) {
var currentObject = ensureNotNull(context.currentObject, 'currentObject'),
propertyName = ensureNotNull(context.propertyName, 'propertyName'),
updatedObjectList = context.updatedObjectList,
objectsToRemove = context.objectsToRemove || [],
objectsToAdd = context.objectsToAdd || [],
changed = [ context.element], // this will not change any diagram-js elements
referencePropertyName;
if (context.referencePropertyName) {
referencePropertyName = context.referencePropertyName;
}
var objectList = currentObject[propertyName];
// adjust array reference in the parent business object
context.previousList = currentObject[propertyName];
if (updatedObjectList) {
currentObject[propertyName] = updatedObjectList;
} else {
var listCopy = [];
// remove all objects which should be removed
forEach(objectList, function(object) {
if (objectsToRemove.indexOf(object) == -1) {
listCopy.push(object);
}
});
// add all objects which should be added
listCopy = listCopy.concat(objectsToAdd);
// set property to new list
if (listCopy.length > 0 || !referencePropertyName) {
// as long as there are elements in the list update the list
currentObject[propertyName] = listCopy;
} else if (referencePropertyName) {
// remove the list when it is empty
var parentObject = currentObject.$parent;
parentObject.set(referencePropertyName, undefined);
}
}
context.changed = changed;
// indicate changed on objects affected by the update
return changed;
};
/**
* Reverts the update
*
* @method CreateBusinessObjectListHandler#revert
*
* @param {Object} context
*
* @return {djs.mode.Base} the updated element
*/
UpdateBusinessObjectListHandler.prototype.revert = function(context) {
var currentObject = context.currentObject,
propertyName = context.propertyName,
previousList = context.previousList,
parentObject = currentObject.$parent;
if (context.referencePropertyName) {
parentObject.set(context.referencePropertyName, currentObject);
}
// remove new element
currentObject.set(propertyName, previousList);
return context.changed;
};

@ -0,0 +1,27 @@
'use strict';
var forEach = require('lodash/forEach');
var HANDLERS = {
'properties-panel.update-businessobject': require('./UpdateBusinessObjectHandler'),
'properties-panel.create-and-reference': require('./CreateAndReferenceHandler'),
'properties-panel.create-businessobject-list': require('./CreateBusinessObjectListHandler'),
'properties-panel.update-businessobject-list': require('./UpdateBusinessObjectListHandler'),
'properties-panel.multi-command-executor': require('./MultiCommandHandler')
};
function CommandInitializer(eventBus, commandStack) {
eventBus.on('diagram.init', function() {
forEach(HANDLERS, function(handler, id) {
commandStack.registerHandler(id, handler);
});
});
}
CommandInitializer.$inject = [ 'eventBus', 'commandStack' ];
module.exports = {
__init__: [ CommandInitializer ]
};

@ -0,0 +1,77 @@
'use strict';
var getBusinessObject = require('bpmn-js/lib/util/ModelUtil').getBusinessObject,
cmdHelper = require('../helper/CmdHelper'),
escapeHTML = require('../Utils').escapeHTML;
var entryFieldDescription = require('./EntryFieldDescription');
var checkbox = function(options, defaultParameters) {
var resource = defaultParameters,
id = resource.id,
label = options.label || id,
canBeDisabled = !!options.disabled && typeof options.disabled === 'function',
canBeHidden = !!options.hidden && typeof options.hidden === 'function',
description = options.description;
resource.html =
'<input id="activiti-' + escapeHTML(id) + '" ' +
'type="checkbox" ' +
'name="' + escapeHTML(options.modelProperty) + '" ' +
(canBeDisabled ? 'data-disable="isDisabled"' : '') +
(canBeHidden ? 'data-show="isHidden"' : '') +
' />' +
'<label for="activiti-' + escapeHTML(id) + '" ' +
(canBeDisabled ? 'data-disable="isDisabled"' : '') +
(canBeHidden ? 'data-show="isHidden"' : '') +
'>' + escapeHTML(label) + '</label>';
// add description below checkbox entry field
if (description) {
resource.html += entryFieldDescription(description);
}
resource.get = function(element) {
var bo = getBusinessObject(element),
res = {};
res[options.modelProperty] = bo.get(options.modelProperty);
return res;
};
resource.set = function(element, values) {
var res = {};
res[options.modelProperty] = !!values[options.modelProperty];
return cmdHelper.updateProperties(element, res);
};
if (typeof options.set === 'function') {
resource.set = options.set;
}
if (typeof options.get === 'function') {
resource.get = options.get;
}
if (canBeDisabled) {
resource.isDisabled = function() {
return options.disabled.apply(resource, arguments);
};
}
if (canBeHidden) {
resource.isHidden = function() {
return !options.hidden.apply(resource, arguments);
};
}
resource.cssClasses = ['bpp-checkbox'];
return resource;
};
module.exports = checkbox;

@ -0,0 +1,118 @@
'use strict';
var assign = require('lodash/assign'),
find = require('lodash/find');
var domQuery = require('min-dom').query;
var escapeHTML = require('../Utils').escapeHTML;
var selectEntryFactory = require('./SelectEntryFactory'),
entryFieldDescription = require('./EntryFieldDescription');
/**
* The combo box is a special implementation of the select entry and adds the option 'custom' to the
* select box. If 'custom' is selected, an additional text input field is shown which allows to define
* a custom value.
*
* @param {Object} options
* @param {string} options.id
* @param {string} options.label
* @param {Array<Object>} options.selectOptions list of name/value pairs
* @param {string} options.modelProperty
* @param {function} options.get
* @param {function} options.set
* @param {string} [options.customValue] custom select option value (default: 'custom')
* @param {string} [options.customName] custom select option name visible in the select box (default: 'custom')
*
* @return {Object}
*/
var comboBox = function(options) {
var selectOptions = options.selectOptions,
modelProperty = options.modelProperty,
customValue = options.customValue || 'custom',
customName = options.customName || 'custom ' + modelProperty,
description = options.description;
// check if a value is not a built in value
var isCustomValue = function(value) {
if (typeof value[modelProperty] === 'undefined') {
return false;
}
var isCustom = !find(selectOptions, function(option) {
return value[modelProperty] === option.value;
});
return isCustom;
};
var comboOptions = assign({}, options);
// true if the selected value in the select box is customValue
comboOptions.showCustomInput = function(element, node) {
var selectBox = domQuery('[data-entry="'+ options.id +'"] select', node.parentNode);
if (selectBox) {
return selectBox.value === customValue;
}
return false;
};
comboOptions.get = function(element, node) {
var value = options.get(element, node);
var modifiedValues = {};
if (!isCustomValue(value)) {
modifiedValues[modelProperty] = value[modelProperty] || '';
return modifiedValues;
}
modifiedValues[modelProperty] = customValue;
modifiedValues['custom-'+modelProperty] = value[modelProperty];
return modifiedValues;
};
comboOptions.set = function(element, values, node) {
var modifiedValues = {};
// if the custom select option has been selected
// take the value from the text input field
if (values[modelProperty] === customValue) {
modifiedValues[modelProperty] = values['custom-' + modelProperty] || '';
}
else if (options.emptyParameter && values[modelProperty] === '') {
modifiedValues[modelProperty] = undefined;
} else {
modifiedValues[modelProperty] = values[modelProperty];
}
return options.set(element, modifiedValues, node);
};
comboOptions.selectOptions.push({ name: customName, value: customValue });
var comboBoxEntry = assign({}, selectEntryFactory(comboOptions, comboOptions));
comboBoxEntry.html += '<div class="bpp-field-wrapper bpp-combo-input" ' +
'data-show="showCustomInput"' +
'>' +
'<input id="activiti-' + escapeHTML(options.id) + '-input" type="text" name="custom-' +
escapeHTML(modelProperty) + '" ' +
' />' +
'</div>';
// add description below combo box entry field
if (description) {
comboBoxEntry.html += entryFieldDescription(description);
}
return comboBoxEntry;
};
module.exports = comboBox;

@ -0,0 +1,163 @@
'use strict';
var getBusinessObject = require('bpmn-js/lib/util/ModelUtil').getBusinessObject;
// input entities
var textInputField = require('./TextInputEntryFactory'),
checkboxField = require('./CheckboxEntryFactory'),
selectBoxField = require('./SelectEntryFactory'),
comboBoxField = require('./ComboEntryFactory'),
textBoxField = require('./TextBoxEntryFactory'),
validationAwareTextInputField = require('./ValidationAwareTextInput'),
tableField = require('./TableEntryFactory'),
labelEntry = require('./LabelFactory'),
link = require('./LinkEntryFactory');
var cmdHelper = require('../helper/CmdHelper');
// helpers ////////////////////////////////////////
function ensureNotNull(prop) {
if (!prop) {
throw new Error(prop + ' must be set.');
}
return prop;
}
/**
* sets the default parameters which are needed to create an entry
*
* @param options
* @returns {{id: *, description: (*|string), get: (*|Function), set: (*|Function),
* validate: (*|Function), html: string}}
*/
var setDefaultParameters = function(options) {
// default method to fetch the current value of the input field
var defaultGet = function(element) {
var bo = getBusinessObject(element),
res = {},
prop = ensureNotNull(options.modelProperty);
res[prop] = bo.get(prop);
return res;
};
// default method to set a new value to the input field
var defaultSet = function(element, values) {
var res = {},
prop = ensureNotNull(options.modelProperty);
if (values[prop] !== '') {
res[prop] = values[prop];
} else {
res[prop] = undefined;
}
return cmdHelper.updateProperties(element, res);
};
// default validation method
var defaultValidate = function() {
return {};
};
return {
id : options.id,
description : (options.description || ''),
get : (options.get || defaultGet),
set : (options.set || defaultSet),
validate : (options.validate || defaultValidate),
html: ''
};
};
function EntryFactory() {
}
/**
* Generates an text input entry object for a property panel.
* options are:
* - id: id of the entry - String
*
* - description: description of the property - String
*
* - label: label for the input field - String
*
* - set: setter method - Function
*
* - get: getter method - Function
*
* - validate: validation mehtod - Function
*
* - modelProperty: name of the model property - String
*
* - buttonAction: Object which contains the following properties: - Object
* ---- name: name of the [data-action] callback - String
* ---- method: callback function for [data-action] - Function
*
* - buttonShow: Object which contains the following properties: - Object
* ---- name: name of the [data-show] callback - String
* ---- method: callback function for [data-show] - Function
*
* @param options
* @returns the propertyPanel entry resource object
*/
EntryFactory.textField = function(options) {
return textInputField(options, setDefaultParameters(options));
};
EntryFactory.validationAwareTextField = function(options) {
return validationAwareTextInputField(options, setDefaultParameters(options));
};
/**
* Generates a checkbox input entry object for a property panel.
* options are:
* - id: id of the entry - String
*
* - description: description of the property - String
*
* - label: label for the input field - String
*
* - set: setter method - Function
*
* - get: getter method - Function
*
* - validate: validation method - Function
*
* - modelProperty: name of the model property - String
*
* @param options
* @returns the propertyPanel entry resource object
*/
EntryFactory.checkbox = function(options) {
return checkboxField(options, setDefaultParameters(options));
};
EntryFactory.textBox = function(options) {
return textBoxField(options, setDefaultParameters(options));
};
EntryFactory.selectBox = function(options) {
return selectBoxField(options, setDefaultParameters(options));
};
EntryFactory.comboBox = function(options) {
return comboBoxField(options);
};
EntryFactory.table = function(options) {
return tableField(options);
};
EntryFactory.label = function(options) {
return labelEntry(options);
};
EntryFactory.link = function(options) {
return link(options);
};
module.exports = EntryFactory;

@ -0,0 +1,49 @@
'use strict';
var escapeHTML = require('../Utils').escapeHTML;
/**
* Create a linkified and HTML escaped entry field description.
*
* As a special feature, this description may contain both markdown
* and plain <a href> links.
*
* @param {String} description
*/
module.exports = function entryFieldDescription(description) {
// we tokenize the description to extract text, HTML and markdown links
// text and links are handled seperately
var escaped = [];
// match markdown [{TEXT}]({URL}) and HTML links <a href="{URL}">{TEXT}</a>
var pattern = /(?:\[([^\]]+)\]\((https?:\/\/[^"<>\]]+)\))|(?:<a href="(https?:\/\/[^"<>]+)">([^<]*)<\/a>)/gi;
var index = 0;
var match;
var link, text;
while ((match = pattern.exec(description))) {
// escape + insert text before match
if (match.index > index) {
escaped.push(escapeHTML(description.substring(index, match.index)));
}
link = match[2] || match[3];
text = match[1] || match[4];
// insert safe link
escaped.push('<a href="' + link + '" target="_blank">' + escapeHTML(text) + '</a>');
index = match.index + match[0].length;
}
// escape and insert text after last match
if (index < description.length) {
escaped.push(escapeHTML(description.substring(index)));
}
return '<div class="bpp-field-description">' + escaped.join('') + '</div>';
};

@ -0,0 +1,38 @@
'use strict';
/**
* The label factory provides a label entry. For the label text
* it expects either a string provided by the options.labelText
* parameter or it could be generated programmatically using a
* function passed as the options.get parameter.
*
* @param {Object} options
* @param {string} options.id
* @param {string} [options.labelText]
* @param {Function} [options.get]
* @param {Function} [options.showLabel]
* @param {Boolean} [options.divider] adds a divider at the top of the label if true; default: false
*/
var label = function(options) {
return {
id: options.id,
html: '<label data-value="label" ' +
'data-show="showLabel" ' +
'class="entry-label' + (options.divider ? ' divider' : '') + '">' +
'</label>',
get: function(element, node) {
if (typeof options.get === 'function') {
return options.get(element, node);
}
return { label: options.labelText };
},
showLabel: function(element, node) {
if (typeof options.showLabel === 'function') {
return options.showLabel(element, node);
}
return true;
}
};
};
module.exports = label;

@ -0,0 +1,78 @@
'use strict';
var escapeHTML = require('../Utils').escapeHTML;
var entryFieldDescription = require('./EntryFieldDescription');
var bind = require('lodash/bind');
/**
* An entry that renders a clickable link.
*
* A passed {@link options#handleClick} handler is responsible
* to process the click.
*
* The link may be conditionally shown or hidden. This can be
* controlled via the {@link options.showLink}.
*
* @param {Object} options
* @param {String} options.id
* @param {String} [options.label]
* @param {Function} options.handleClick
* @param {Function} [options.showLink] returning false to hide link
* @param {String} [options.description]
*
* @example
*
* var linkEntry = link({
* id: 'foo',
* description: 'Some Description',
* handleClick: function(element, node, event) { ... },
* showLink: function(element, node) { ... }
* });
*
* @return {Entry} the newly created entry
*/
function link(options) {
var id = options.id,
label = options.label || id,
showLink = options.showLink,
handleClick = options.handleClick,
description = options.description;
if (showLink && typeof showLink !== 'function') {
throw new Error('options.showLink must be a function');
}
if (typeof handleClick !== 'function') {
throw new Error('options.handleClick must be a function');
}
var resource = {
id: id
};
resource.html =
'<a data-action="handleClick" ' +
(showLink ? 'data-show="showLink" ' : '') +
'class="bpp-entry-link' + (options.cssClasses ? ' ' + escapeHTML(options.cssClasses) : '') +
'">' + escapeHTML(label) + '</a>';
// add description below link entry field
if (description) {
resource.html += entryFieldDescription(description);
}
resource.handleClick = bind(handleClick, resource);
if (typeof showLink === 'function') {
resource.showLink = function() {
return showLink.apply(resource, arguments);
};
}
return resource;
}
module.exports = link;

@ -0,0 +1,137 @@
'use strict';
var escapeHTML = require('../Utils').escapeHTML;
var domify = require('min-dom').domify;
var forEach = require('lodash/forEach');
var entryFieldDescription = require('./EntryFieldDescription');
var isList = function(list) {
return !(!list || Object.prototype.toString.call(list) !== '[object Array]');
};
var addEmptyParameter = function(list) {
return list.concat([ { name: '', value: '' } ]);
};
var createOption = function(option) {
return '<option value="' + option.value + '">' + option.name + '</option>';
};
/**
* @param {Object} options
* @param {string} options.id
* @param {string} [options.label]
* @param {Array<Object>} options.selectOptions
* @param {string} options.modelProperty
* @param {boolean} options.emptyParameter
* @param {function} options.disabled
* @param {function} options.hidden
* @param {Object} defaultParameters
*
* @return {Object}
*/
var selectbox = function(options, defaultParameters) {
var resource = defaultParameters,
label = options.label || resource.id,
selectOptions = options.selectOptions || [ { name: '', value: '' } ],
modelProperty = options.modelProperty,
emptyParameter = options.emptyParameter,
canBeDisabled = !!options.disabled && typeof options.disabled === 'function',
canBeHidden = !!options.hidden && typeof options.hidden === 'function',
description = options.description;
if (emptyParameter) {
selectOptions = addEmptyParameter(selectOptions);
}
resource.html =
'<label for="activiti-' + escapeHTML(resource.id) + '"' +
(canBeDisabled ? 'data-disable="isDisabled" ' : '') +
(canBeHidden ? 'data-show="isHidden" ' : '') +
'>' + escapeHTML(label) + '</label>' +
'<select id="activiti-' + escapeHTML(resource.id) + '-select" name="' +
escapeHTML(modelProperty) + '"' +
(canBeDisabled ? 'data-disable="isDisabled" ' : '') +
(canBeHidden ? 'data-show="isHidden" ' : '') +
' data-value>';
if (isList(selectOptions)) {
forEach(selectOptions, function(option) {
resource.html += '<option value="' + escapeHTML(option.value) + '">' +
(option.name ? escapeHTML(option.name) : '') + '</option>';
});
}
resource.html += '</select>';
// add description below select box entry field
if (description && typeof options.showCustomInput !== 'function') {
resource.html += entryFieldDescription(description);
}
/**
* Fill the select box options dynamically.
*
* Calls the defined function #selectOptions in the entry to get the
* values for the options and set the value to the inputNode.
*
* @param {djs.model.Base} element
* @param {HTMLElement} entryNode
* @param {EntryDescriptor} inputNode
* @param {Object} inputName
* @param {Object} newValue
*/
resource.setControlValue = function(element, entryNode, inputNode, inputName, newValue) {
if (typeof selectOptions === 'function') {
var options = selectOptions(element, inputNode);
if (options) {
// remove existing options
while (inputNode.firstChild) {
inputNode.removeChild(inputNode.firstChild);
}
// add options
forEach(options, function(option) {
var template = domify(createOption(option));
inputNode.appendChild(template);
});
}
}
// set select value
if (newValue !== undefined) {
inputNode.value = newValue;
}
};
if (canBeDisabled) {
resource.isDisabled = function() {
return options.disabled.apply(resource, arguments);
};
}
if (canBeHidden) {
resource.isHidden = function() {
return !options.hidden.apply(resource, arguments);
};
}
resource.cssClasses = ['bpp-dropdown'];
return resource;
};
module.exports = selectbox;

@ -0,0 +1,338 @@
'use strict';
var escapeHTML = require('../Utils').escapeHTML;
var cmdHelper = require('../helper/CmdHelper');
var domQuery = require('min-dom').query,
domAttr = require('min-dom').attr,
domClosest = require('min-dom').closest;
var filter = require('lodash/filter'),
forEach = require('lodash/forEach'),
keys = require('lodash/keys');
var domify = require('min-dom').domify;
var entryFieldDescription = require('./EntryFieldDescription');
var updateSelection = require('selection-update');
var TABLE_ROW_DIV_SNIPPET = '<div class="bpp-field-wrapper bpp-table-row">';
var DELETE_ROW_BUTTON_SNIPPET = '<button class="clear" data-action="deleteElement">' +
'<span>X</span>' +
'</button>';
function createInputRowTemplate(properties, canRemove) {
var template = TABLE_ROW_DIV_SNIPPET;
template += createInputTemplate(properties, canRemove);
template += canRemove ? DELETE_ROW_BUTTON_SNIPPET : '';
template += '</div>';
return template;
}
function createInputTemplate(properties, canRemove) {
var columns = properties.length;
var template = '';
forEach(properties, function(prop) {
template += '<input class="bpp-table-row-columns-' + columns + ' ' +
(canRemove ? 'bpp-table-row-removable' : '') + '" ' +
'id="activiti-table-row-cell-input-value" ' +
'type="text" ' +
'name="' + escapeHTML(prop) + '" />';
});
return template;
}
function createLabelRowTemplate(labels) {
var template = TABLE_ROW_DIV_SNIPPET;
template += createLabelTemplate(labels);
template += '</div>';
return template;
}
function createLabelTemplate(labels) {
var columns = labels.length;
var template = '';
forEach(labels, function(label) {
template += '<label class="bpp-table-row-columns-' + columns + '">' + escapeHTML(label) + '</label>';
});
return template;
}
function pick(elements, properties) {
return (elements || []).map(function(elem) {
var newElement = {};
forEach(properties, function(prop) {
newElement[prop] = elem[prop] || '';
});
return newElement;
});
}
function diff(element, node, values, oldValues, editable) {
return filter(values, function(value, idx) {
return !valueEqual(element, node, value, oldValues[idx], editable, idx);
});
}
function valueEqual(element, node, value, oldValue, editable, idx) {
if (value && !oldValue) {
return false;
}
var allKeys = keys(value).concat(keys(oldValue));
return allKeys.every(function(key) {
var n = value[key] || undefined;
var o = oldValue[key] || undefined;
return !editable(element, node, key, idx) || n === o;
});
}
function getEntryNode(node) {
return domClosest(node, '[data-entry]', true);
}
function getContainer(node) {
return domQuery('div[data-list-entry-container]', node);
}
function getSelection(node) {
return {
start: node.selectionStart,
end: node.selectionEnd
};
}
function setSelection(node, selection) {
node.selectionStart = selection.start;
node.selectionEnd = selection.end;
}
/**
* @param {Object} options
* @param {string} options.id
* @param {string} options.description
* @param {Array<string>} options.modelProperties
* @param {Array<string>} options.labels
* @param {Function} options.getElements - this callback function must return a list of business object items
* @param {Function} options.removeElement
* @param {Function} options.addElement
* @param {Function} options.updateElement
* @param {Function} options.editable
* @param {Function} options.setControlValue
* @param {Function} options.show
*
* @return {Object}
*/
module.exports = function(options) {
var id = options.id,
modelProperties = options.modelProperties,
labels = options.labels,
description = options.description;
var labelRow = createLabelRowTemplate(labels);
var getElements = options.getElements;
var removeElement = options.removeElement,
canRemove = typeof removeElement === 'function';
var addElement = options.addElement,
canAdd = typeof addElement === 'function',
addLabel = options.addLabel || 'Add Value';
var updateElement = options.updateElement,
canUpdate = typeof updateElement === 'function';
var editable = options.editable || function() { return true; },
setControlValue = options.setControlValue;
var show = options.show,
canBeShown = typeof show === 'function';
var elements = function(element, node) {
return pick(getElements(element, node), modelProperties);
};
var factory = {
id: id,
html: (canAdd ?
'<div class="bpp-table-add-row" ' + (canBeShown ? 'data-show="show"' : '') + '>' +
'<label>' + escapeHTML(addLabel) + '</label>' +
'<button class="add" data-action="addElement"><span>+</span></button>' +
'</div>' : '') +
'<div class="bpp-table" data-show="showTable">' +
'<div class="bpp-field-wrapper bpp-table-row">' +
labelRow +
'</div>' +
'<div data-list-entry-container>' +
'</div>' +
'</div>' +
// add description below table entry field
(description ? entryFieldDescription(description) : ''),
get: function(element, node) {
var boElements = elements(element, node, this.__invalidValues);
var invalidValues = this.__invalidValues;
delete this.__invalidValues;
forEach(invalidValues, function(value, idx) {
var element = boElements[idx];
forEach(modelProperties, function(prop) {
element[prop] = value[prop];
});
});
return boElements;
},
set: function(element, values, node) {
var action = this.__action || {};
delete this.__action;
if (action.id === 'delete-element') {
return removeElement(element, node, action.idx);
}
else if (action.id === 'add-element') {
return addElement(element, node);
}
else if (canUpdate) {
var commands = [],
valuesToValidate = values;
if (typeof options.validate !== 'function') {
valuesToValidate = diff(element, node, values, elements(element, node), editable);
}
var self = this;
forEach(valuesToValidate, function(value) {
var validationError,
idx = values.indexOf(value);
if (typeof options.validate === 'function') {
validationError = options.validate(element, value, node, idx);
}
if (!validationError) {
var cmd = updateElement(element, value, node, idx);
if (cmd) {
commands.push(cmd);
}
} else {
// cache invalid value in an object by index as key
self.__invalidValues = self.__invalidValues || {};
self.__invalidValues[idx] = value;
// execute a command, which does not do anything
commands.push(cmdHelper.updateProperties(element, {}));
}
});
return commands;
}
},
createListEntryTemplate: function(value, index, selectBox) {
return createInputRowTemplate(modelProperties, canRemove);
},
addElement: function(element, node, event, scopeNode) {
var template = domify(createInputRowTemplate(modelProperties, canRemove));
var container = getContainer(node);
container.appendChild(template);
this.__action = {
id: 'add-element'
};
return true;
},
deleteElement: function(element, node, event, scopeNode) {
var container = getContainer(node);
var rowToDelete = event.delegateTarget.parentNode;
var idx = parseInt(domAttr(rowToDelete, 'data-index'), 10);
container.removeChild(rowToDelete);
this.__action = {
id: 'delete-element',
idx: idx
};
return true;
},
editable: function(element, rowNode, input, prop, value, idx) {
var entryNode = domClosest(rowNode, '[data-entry]');
return editable(element, entryNode, prop, idx);
},
show: function(element, entryNode, node, scopeNode) {
entryNode = getEntryNode(entryNode);
return show(element, entryNode, node, scopeNode);
},
showTable: function(element, entryNode, node, scopeNode) {
entryNode = getEntryNode(entryNode);
var elems = elements(element, entryNode);
return elems && elems.length && (!canBeShown || show(element, entryNode, node, scopeNode));
},
validateListItem: function(element, value, node, idx) {
if (typeof options.validate === 'function') {
return options.validate(element, value, node, idx);
}
}
};
// Update/set the selection on the correct position.
// It's the same code like for an input value in the PropertiesPanel.js.
if (setControlValue) {
factory.setControlValue = function(element, rowNode, input, prop, value, idx) {
var entryNode = getEntryNode(rowNode);
var isReadOnly = domAttr(input, 'readonly');
var oldValue = input.value;
var selection;
// prevents input fields from having the value 'undefined'
if (value === undefined) {
value = '';
}
// when the attribute 'readonly' exists, ignore the comparison
// with 'oldValue' and 'value'
if (!!isReadOnly && oldValue === value) {
return;
}
// update selection on undo/redo
if (document.activeElement === input) {
selection = updateSelection(getSelection(input), oldValue, value);
}
setControlValue(element, entryNode, input, prop, value, idx);
if (selection) {
setSelection(input, selection);
}
};
}
return factory;
};

@ -0,0 +1,42 @@
'use strict';
var escapeHTML = require('../Utils').escapeHTML;
var entryFieldDescription = require('./EntryFieldDescription');
var textBox = function(options, defaultParameters) {
var resource = defaultParameters,
label = options.label || resource.id,
canBeShown = !!options.show && typeof options.show === 'function',
description = options.description;
resource.html =
'<label for="activiti-' + escapeHTML(resource.id) + '" ' +
(canBeShown ? 'data-show="isShown"' : '') +
'>' + label + '</label>' +
'<div class="bpp-field-wrapper" ' +
(canBeShown ? 'data-show="isShown"' : '') +
'>' +
'<div contenteditable="true" id="activiti-' + escapeHTML(resource.id) + '" ' +
'name="' + escapeHTML(options.modelProperty) + '" />' +
'</div>';
// add description below text box entry field
if (description) {
resource.html += entryFieldDescription(description);
}
if (canBeShown) {
resource.isShown = function() {
return options.show.apply(resource, arguments);
};
}
resource.cssClasses = ['bpp-textbox'];
return resource;
};
module.exports = textBox;

@ -0,0 +1,85 @@
'use strict';
var escapeHTML = require('../Utils').escapeHTML;
var domQuery = require('min-dom').query;
var entryFieldDescription = require('./EntryFieldDescription');
var textField = function(options, defaultParameters) {
// Default action for the button next to the input-field
var defaultButtonAction = function(element, inputNode) {
var input = domQuery('input[name="' + options.modelProperty + '"]', inputNode);
input.value = '';
return true;
};
// default method to determine if the button should be visible
var defaultButtonShow = function(element, inputNode) {
var input = domQuery('input[name="' + options.modelProperty + '"]', inputNode);
return input.value !== '';
};
var resource = defaultParameters,
label = options.label || resource.id,
dataValueLabel = options.dataValueLabel,
buttonLabel = (options.buttonLabel || 'X'),
actionName = (typeof options.buttonAction != 'undefined') ? options.buttonAction.name : 'clear',
actionMethod = (typeof options.buttonAction != 'undefined') ? options.buttonAction.method : defaultButtonAction,
showName = (typeof options.buttonShow != 'undefined') ? options.buttonShow.name : 'canClear',
showMethod = (typeof options.buttonShow != 'undefined') ? options.buttonShow.method : defaultButtonShow,
canBeDisabled = !!options.disabled && typeof options.disabled === 'function',
canBeHidden = !!options.hidden && typeof options.hidden === 'function',
description = options.description;
resource.html =
'<label for="activiti-' + escapeHTML(resource.id) + '" ' +
(canBeDisabled ? 'data-disable="isDisabled" ' : '') +
(canBeHidden ? 'data-show="isHidden" ' : '') +
(dataValueLabel ? 'data-value="' + escapeHTML(dataValueLabel) + '"' : '') + '>'+ escapeHTML(label) +'</label>' +
'<div class="bpp-field-wrapper" ' +
(canBeDisabled ? 'data-disable="isDisabled"' : '') +
(canBeHidden ? 'data-show="isHidden"' : '') +
'>' +
'<input id="activiti-' + escapeHTML(resource.id) + '" type="text" name="' + escapeHTML(options.modelProperty) + '" ' +
(canBeDisabled ? 'data-disable="isDisabled"' : '') +
(canBeHidden ? 'data-show="isHidden"' : '') +
' />' +
'<button class="' + escapeHTML(actionName) + '" data-action="' + escapeHTML(actionName) + '" data-show="' + escapeHTML(showName) + '" ' +
(canBeDisabled ? 'data-disable="isDisabled"' : '') +
(canBeHidden ? ' data-show="isHidden"' : '') + '>' +
'<span>' + escapeHTML(buttonLabel) + '</span>' +
'</button>' +
'</div>';
// add description below text input entry field
if (description) {
resource.html += entryFieldDescription(description);
}
resource[actionName] = actionMethod;
resource[showName] = showMethod;
if (canBeDisabled) {
resource.isDisabled = function() {
return options.disabled.apply(resource, arguments);
};
}
if (canBeHidden) {
resource.isHidden = function() {
return !options.hidden.apply(resource, arguments);
};
}
resource.cssClasses = ['bpp-textfield'];
return resource;
};
module.exports = textField;

@ -0,0 +1,56 @@
'use strict';
var textField = require('./TextInputEntryFactory');
/**
* This function is a wrapper around TextInputEntryFactory.
* It adds functionality to cache an invalid value entered in the
* text input, instead of setting it on the business object.
*/
var validationAwareTextField = function(options, defaultParameters) {
var modelProperty = options.modelProperty;
defaultParameters.get = function(element, node) {
var value = this.__lastInvalidValue;
delete this.__lastInvalidValue;
var properties = {};
properties[modelProperty] = value !== undefined ? value : options.getProperty(element, node);
return properties;
};
defaultParameters.set = function(element, values, node) {
var validationErrors = validate.apply(this, [ element, values, node ]),
propertyValue = values[modelProperty];
// make sure we do not update the id
if (validationErrors && validationErrors[modelProperty]) {
this.__lastInvalidValue = propertyValue;
return options.setProperty(element, {}, node);
} else {
var properties = {};
properties[modelProperty] = propertyValue;
return options.setProperty(element, properties, node);
}
};
var validate = defaultParameters.validate = function(element, values, node) {
var value = values[modelProperty] || this.__lastInvalidValue;
var property = {};
property[modelProperty] = value;
return options.validate(element, property, node);
};
return textField(options, defaultParameters);
};
module.exports = validationAwareTextField;

@ -0,0 +1,76 @@
'use strict';
var map = require('lodash/map');
var extensionElementsHelper = require('./ExtensionElementsHelper');
/**
* Returns true if the attribute 'activiti:asyncBefore' is set
* to true.
*
* @param {ModdleElement} bo
*
* @return {boolean} a boolean value
*/
function isAsyncBefore(bo) {
return !!(bo.get('activiti:asyncBefore') || bo.get('activiti:async'));
}
module.exports.isAsyncBefore = isAsyncBefore;
/**
* Returns true if the attribute 'activiti:asyncAfter' is set
* to true.
*
* @param {ModdleElement} bo
*
* @return {boolean} a boolean value
*/
function isAsyncAfter(bo) {
return !!bo.get('activiti:asyncAfter');
}
module.exports.isAsyncAfter = isAsyncAfter;
/**
* Returns true if the attribute 'activiti:exclusive' is set
* to true.
*
* @param {ModdleElement} bo
*
* @return {boolean} a boolean value
*/
function isExclusive(bo) {
return !!bo.get('activiti:exclusive');
}
module.exports.isExclusive = isExclusive;
/**
* Get first 'activiti:FailedJobRetryTimeCycle' from the business object.
*
* @param {ModdleElement} bo
*
* @return {Array<ModdleElement>} a list of 'activiti:FailedJobRetryTimeCycle'
*/
function getFailedJobRetryTimeCycle(bo) {
return (extensionElementsHelper.getExtensionElements(bo, 'activiti:FailedJobRetryTimeCycle') || [])[0];
}
module.exports.getFailedJobRetryTimeCycle = getFailedJobRetryTimeCycle;
/**
* Removes all existing 'activiti:FailedJobRetryTimeCycle' from the business object
*
* @param {ModdleElement} bo
*
* @return {Array<ModdleElement>} a list of 'activiti:FailedJobRetryTimeCycle'
*/
function removeFailedJobRetryTimeCycle(bo, element) {
var retryTimeCycles = extensionElementsHelper.getExtensionElements(bo, 'activiti:FailedJobRetryTimeCycle');
return map(retryTimeCycles, function(cycle) {
return extensionElementsHelper.removeEntry(bo, element, cycle);
});
}
module.exports.removeFailedJobRetryTimeCycle = removeFailedJobRetryTimeCycle;

@ -0,0 +1,31 @@
'use strict';
var collectionAdd = require('diagram-js/lib/util/Collections').add,
getBusinessObject = require('bpmn-js/lib/util/ModelUtil').getBusinessObject;
var CategoryHelper = {};
module.exports = CategoryHelper;
/**
* Creates a new bpmn:CategoryValue inside a new bpmn:Category
*
* @param {ModdleElement} definitions
* @param {BpmnFactory} bpmnFactory
*
* @return {ModdleElement} categoryValue.
*/
CategoryHelper.createCategoryValue = function(definitions, bpmnFactory) {
var categoryValue = bpmnFactory.create('bpmn:CategoryValue'),
category = bpmnFactory.create('bpmn:Category', {
categoryValue: [ categoryValue ]
});
// add to correct place
collectionAdd(definitions.get('rootElements'), category);
getBusinessObject(category).$parent = definitions;
getBusinessObject(categoryValue).$parent = category;
return categoryValue;
};

@ -0,0 +1,77 @@
'use strict';
var CmdHelper = {};
module.exports = CmdHelper;
CmdHelper.updateProperties = function(element, properties) {
return {
cmd: 'element.updateProperties',
context: { element: element, properties: properties }
};
};
CmdHelper.updateBusinessObject = function(element, businessObject, newProperties) {
return {
cmd: 'properties-panel.update-businessobject',
context: {
element: element,
businessObject: businessObject,
properties: newProperties
}
};
};
CmdHelper.addElementsTolist = function(element, businessObject, listPropertyName, objectsToAdd) {
return {
cmd: 'properties-panel.update-businessobject-list',
context: {
element: element,
currentObject: businessObject,
propertyName: listPropertyName,
objectsToAdd: objectsToAdd
}
};
};
CmdHelper.removeElementsFromList = function(element, businessObject, listPropertyName, referencePropertyName, objectsToRemove) {
return {
cmd: 'properties-panel.update-businessobject-list',
context: {
element: element,
currentObject: businessObject,
propertyName: listPropertyName,
referencePropertyName: referencePropertyName,
objectsToRemove: objectsToRemove
}
};
};
CmdHelper.addAndRemoveElementsFromList = function(element, businessObject, listPropertyName, referencePropertyName, objectsToAdd, objectsToRemove) {
return {
cmd: 'properties-panel.update-businessobject-list',
context: {
element: element,
currentObject: businessObject,
propertyName: listPropertyName,
referencePropertyName: referencePropertyName,
objectsToAdd: objectsToAdd,
objectsToRemove: objectsToRemove
}
};
};
CmdHelper.setList = function(element, businessObject, listPropertyName, updatedObjectList) {
return {
cmd: 'properties-panel.update-businessobject-list',
context: {
element: element,
currentObject: businessObject,
propertyName: listPropertyName,
updatedObjectList: updatedObjectList
}
};
};

@ -0,0 +1,23 @@
'use strict';
var ElementHelper = {};
module.exports = ElementHelper;
/**
* Creates a new element and set the parent to it
*
* @method ElementHelper#createElement
*
* @param {String} elementType of the new element
* @param {Object} properties of the new element in key-value pairs
* @param {moddle.object} parent of the new element
* @param {BpmnFactory} factory which creates the new element
*
* @returns {djs.model.Base} element which is created
*/
ElementHelper.createElement = function(elementType, properties, parent, factory) {
var element = factory.create(elementType, properties);
element.$parent = parent;
return element;
};

@ -0,0 +1,57 @@
'use strict';
var getBusinessObject = require('bpmn-js/lib/util/ModelUtil').getBusinessObject,
is = require('bpmn-js/lib/util/ModelUtil').is,
forEach = require('lodash/forEach');
var EventDefinitionHelper = {};
module.exports = EventDefinitionHelper;
EventDefinitionHelper.getEventDefinition = function(element, eventType) {
var bo = getBusinessObject(element),
eventDefinition = null;
if (bo.eventDefinitions) {
forEach(bo.eventDefinitions, function(event) {
if (is(event, eventType)) {
eventDefinition = event;
}
});
}
return eventDefinition;
};
EventDefinitionHelper.getTimerEventDefinition = function(element) {
return this.getEventDefinition(element, 'bpmn:TimerEventDefinition');
};
EventDefinitionHelper.getMessageEventDefinition = function(element) {
return this.getEventDefinition(element, 'bpmn:MessageEventDefinition');
};
EventDefinitionHelper.getSignalEventDefinition = function(element) {
return this.getEventDefinition(element, 'bpmn:SignalEventDefinition');
};
EventDefinitionHelper.getErrorEventDefinition = function(element) {
return this.getEventDefinition(element, 'bpmn:ErrorEventDefinition');
};
EventDefinitionHelper.getEscalationEventDefinition = function(element) {
return this.getEventDefinition(element, 'bpmn:EscalationEventDefinition');
};
EventDefinitionHelper.getCompensateEventDefinition = function(element) {
return this.getEventDefinition(element, 'bpmn:CompensateEventDefinition');
};
EventDefinitionHelper.getLinkEventDefinition = function(element) {
return this.getEventDefinition(element, 'bpmn:LinkEventDefinition');
};
EventDefinitionHelper.getConditionalEventDefinition = function(element) {
return this.getEventDefinition(element, 'bpmn:ConditionalEventDefinition');
};

@ -0,0 +1,54 @@
'use strict';
var cmdHelper = require('./CmdHelper'),
elementHelper = require('./ElementHelper');
var is = require('bpmn-js/lib/util/ModelUtil').is;
var ExtensionElementsHelper = {};
var getExtensionElements = function(bo) {
return bo.get('extensionElements');
};
ExtensionElementsHelper.getExtensionElements = function(bo, type) {
var extensionElements = getExtensionElements(bo);
if (typeof extensionElements !== 'undefined') {
var extensionValues = extensionElements.get('values');
if (typeof extensionValues !== 'undefined') {
var elements = extensionValues.filter(function(value) {
return is(value, type);
});
if (elements.length) {
return elements;
}
}
}
};
ExtensionElementsHelper.addEntry = function(bo, element, entry, bpmnFactory) {
var extensionElements = bo.get('extensionElements');
// if there is no extensionElements list, create one
if (!extensionElements) {
extensionElements = elementHelper.createElement('bpmn:ExtensionElements', { values: [entry] }, bo, bpmnFactory);
return { extensionElements : extensionElements };
} else {
// add new failedJobRetryExtensionElement to existing extensionElements list
return cmdHelper.addElementsTolist(element, extensionElements, 'values', [entry]);
}
};
ExtensionElementsHelper.removeEntry = function(bo, element, entry) {
var extensionElements = bo.get('extensionElements');
if (!extensionElements) {
// return an empty command when there is no extensionElements list
return {};
}
return cmdHelper.removeElementsFromList(element, extensionElements, 'values', 'extensionElements', [entry]);
};
module.exports = ExtensionElementsHelper;

@ -0,0 +1,97 @@
'use strict';
var getBusinessObject = require('bpmn-js/lib/util/ModelUtil').getBusinessObject,
getExtensionElements = require('./ExtensionElementsHelper').getExtensionElements;
var FormHelper = {};
module.exports = FormHelper;
/**
* Return form data from business object or undefined if none exist
*
* @param {djs.model.Base} element
*
* @return {ModdleElement|undefined} formData
*
* 此方法废弃
*
*/
FormHelper.getFormData = function(element) {
/* var bo = getBusinessObject(element);
var formFields = getExtensionElements(bo, 'activiti:FormProperty');
var formData = {}
if (typeof formData !== 'undefined') {
return formData[0];
}*/
return {};
};
/**
* Return all form fields existing in the business object, and
* an empty array if none exist.
*
* @param {djs.model.Base} element
*
* @return {Array} a list of form field objects
*/
FormHelper.getFormFields = function(element) {
/**直接获取 ExtensionElements的 activiti:FormProperty元素*/
var bo = getBusinessObject(element);
var formFields = getExtensionElements(bo, 'activiti:FormProperty');
return formFields || [];
};
/**
* Get a form field from the business object at given index
*
* @param {djs.model.Base} element
* @param {number} idx
*
* @return {ModdleElement} the form field
*/
FormHelper.getFormField = function(element, idx) {
var formFields = this.getFormFields(element);
return formFields[idx];
};
/**
* Get all constraints for a specific form field from the business object
*
* @param {ModdleElement} formField
*
* @return {Array<ModdleElement>} a list of constraint objects
*/
FormHelper.getConstraints = function(formField) {
if (formField && formField.validation && formField.validation.constraints) {
return formField.validation.constraints;
}
return [];
};
/**
* Get all activiti:value objects for a specific form field from the business object
*
* @param {ModdleElement} formField
*
* @return {Array<ModdleElement>} a list of activiti:value objects
*/
FormHelper.getEnumValues = function(formField) {
if (formField && formField.values) {
return formField.values;
}
return [];
};

@ -0,0 +1,192 @@
'use strict';
var ModelUtil = require('bpmn-js/lib/util/ModelUtil'),
is = ModelUtil.is,
getBusinessObject = ModelUtil.getBusinessObject;
var eventDefinitionHelper = require('./EventDefinitionHelper');
var extensionsElementHelper = require('./ExtensionElementsHelper');
var ImplementationTypeHelper = {};
module.exports = ImplementationTypeHelper;
/**
* Returns 'true' if the given element is 'activiti:ServiceTaskLike'
*
* @param {djs.model.Base} element
*
* @return {boolean} a boolean value
*/
ImplementationTypeHelper.isServiceTaskLike = function(element) {
return is(element, 'activiti:ServiceTaskLike');
};
/**
* Returns 'true' if the given element is 'activiti:DmnCapable'
*
* @param {djs.model.Base} element
*
* @return {boolean} a boolean value
*/
ImplementationTypeHelper.isDmnCapable = function(element) {
return is(element, 'activiti:DmnCapable');
};
/**
* Returns 'true' if the given element is 'activiti:ExternalCapable'
*
* @param {djs.model.Base} element
*
* @return {boolean} a boolean value
*/
ImplementationTypeHelper.isExternalCapable = function(element) {
return is(element, 'activiti:ExternalCapable');
};
/**
* Returns 'true' if the given element is 'activiti:TaskListener'
*
* @param {djs.model.Base} element
*
* @return {boolean} a boolean value
*/
ImplementationTypeHelper.isTaskListener = function(element) {
return is(element, 'activiti:TaskListener');
};
/**
* Returns 'true' if the given element is 'activiti:ExecutionListener'
*
* @param {djs.model.Base} element
*
* @return {boolean} a boolean value
*/
ImplementationTypeHelper.isExecutionListener = function(element) {
return is(element, 'activiti:ExecutionListener');
};
/**
* Returns 'true' if the given element is 'activiti:ExecutionListener' or
* 'activiti:TaskListener'
*
* @param {djs.model.Base} element
*
* @return {boolean} a boolean value
*/
ImplementationTypeHelper.isListener = function(element) {
return this.isTaskListener(element) || this.isExecutionListener(element);
};
/**
* Returns 'true' if the given element is 'bpmn:SequenceFlow'
*
* @param {djs.model.Base} element
*
* @return {boolean} a boolean value
*/
ImplementationTypeHelper.isSequenceFlow = function(element) {
return is(element, 'bpmn:SequenceFlow');
};
/**
* Get a 'activiti:ServiceTaskLike' business object.
*
* If the given element is not a 'activiti:ServiceTaskLike', then 'false'
* is returned.
*
* @param {djs.model.Base} element
*
* @return {ModdleElement} the 'activiti:ServiceTaskLike' business object
*/
ImplementationTypeHelper.getServiceTaskLikeBusinessObject = function(element) {
if (is(element, 'bpmn:IntermediateThrowEvent') || is(element, 'bpmn:EndEvent')) {
// change business object to 'messageEventDefinition' when
// the element is a message intermediate throw event or message end event
// because the activiti extensions (e.g. activiti:class) are in the message
// event definition tag and not in the intermediate throw event or end event tag
var messageEventDefinition = eventDefinitionHelper.getMessageEventDefinition(element);
if (messageEventDefinition) {
element = messageEventDefinition;
}
}
return this.isServiceTaskLike(element) && getBusinessObject(element);
};
/**
* Returns the implementation type of the given element.
*
* Possible implementation types are:
* - dmn
* - connector
* - external
* - class
* - expression
* - delegateExpression
* - script
* - or undefined, when no matching implementation type is found
*
* @param {djs.model.Base} element
*
* @return {String} the implementation type
*/
ImplementationTypeHelper.getImplementationType = function(element) {
var bo = this.getServiceTaskLikeBusinessObject(element);
if (!bo) {
if (this.isListener(element)) {
bo = element;
} else {
return;
}
}
if (this.isDmnCapable(bo)) {
var decisionRef = bo.get('activiti:decisionRef');
if (typeof decisionRef !== 'undefined') {
return 'dmn';
}
}
if (this.isServiceTaskLike(bo)) {
var connectors = extensionsElementHelper.getExtensionElements(bo, 'activiti:Connector');
if (typeof connectors !== 'undefined') {
return 'connector';
}
}
if (this.isExternalCapable(bo)) {
var type = bo.get('activiti:type');
if (type === 'external') {
return 'external';
}
}
var cls = bo.get('activiti:class');
if (typeof cls !== 'undefined') {
return 'class';
}
var expression = bo.get('activiti:expression');
if (typeof expression !== 'undefined') {
return 'expression';
}
var delegateExpression = bo.get('activiti:delegateExpression');
if (typeof delegateExpression !== 'undefined') {
return 'delegateExpression';
}
if (this.isListener(bo)) {
var script = bo.get('script');
if (typeof script !== 'undefined') {
return 'script';
}
}
};

@ -0,0 +1,144 @@
'use strict';
var ModelUtil = require('bpmn-js/lib/util/ModelUtil'),
is = ModelUtil.is,
getBusinessObject = ModelUtil.getBusinessObject;
var extensionElementsHelper = require('./ExtensionElementsHelper'),
implementationTypeHelper = require('./ImplementationTypeHelper');
var InputOutputHelper = {};
module.exports = InputOutputHelper;
function getElements(bo, type, prop) {
var elems = extensionElementsHelper.getExtensionElements(bo, type) || [];
return !prop ? elems : (elems[0] || {})[prop] || [];
}
function getParameters(element, prop, insideConnector) {
var inputOutput = InputOutputHelper.getInputOutput(element, insideConnector);
return (inputOutput && inputOutput.get(prop)) || [];
}
/**
* Get a inputOutput from the business object
*
* @param {djs.model.Base} element
* @param {boolean} insideConnector
*
* @return {ModdleElement} the inputOutput object
*/
InputOutputHelper.getInputOutput = function(element, insideConnector) {
if (!insideConnector) {
var bo = getBusinessObject(element);
return (getElements(bo, 'activiti:InputOutput') || [])[0];
}
var connector = this.getConnector(element);
return connector && connector.get('inputOutput');
};
/**
* Get a connector from the business object
*
* @param {djs.model.Base} element
*
* @return {ModdleElement} the connector object
*/
InputOutputHelper.getConnector = function(element) {
var bo = implementationTypeHelper.getServiceTaskLikeBusinessObject(element);
return bo && (getElements(bo, 'activiti:Connector') || [])[0];
};
/**
* Return all input parameters existing in the business object, and
* an empty array if none exist.
*
* @param {djs.model.Base} element
* @param {boolean} insideConnector
*
* @return {Array} a list of input parameter objects
*/
InputOutputHelper.getInputParameters = function(element, insideConnector) {
return getParameters.apply(this, [ element, 'inputParameters', insideConnector ]);
};
/**
* Return all output parameters existing in the business object, and
* an empty array if none exist.
*
* @param {djs.model.Base} element
* @param {boolean} insideConnector
*
* @return {Array} a list of output parameter objects
*/
InputOutputHelper.getOutputParameters = function(element, insideConnector) {
return getParameters.apply(this, [ element, 'outputParameters', insideConnector ]);
};
/**
* Get a input parameter from the business object at given index
*
* @param {djs.model.Base} element
* @param {boolean} insideConnector
* @param {number} idx
*
* @return {ModdleElement} input parameter
*/
InputOutputHelper.getInputParameter = function(element, insideConnector, idx) {
return this.getInputParameters(element, insideConnector)[idx];
};
/**
* Get a output parameter from the business object at given index
*
* @param {djs.model.Base} element
* @param {boolean} insideConnector
* @param {number} idx
*
* @return {ModdleElement} output parameter
*/
InputOutputHelper.getOutputParameter = function(element, insideConnector, idx) {
return this.getOutputParameters(element, insideConnector)[idx];
};
/**
* Returns 'true' if the given element supports inputOutput
*
* @param {djs.model.Base} element
* @param {boolean} insideConnector
*
* @return {boolean} a boolean value
*/
InputOutputHelper.isInputOutputSupported = function(element, insideConnector) {
if (insideConnector) {
return true;
}
var bo = getBusinessObject(element);
return (
is(bo, 'bpmn:FlowNode') && !(
is(bo, 'bpmn:StartEvent') ||
is(bo, 'bpmn:Gateway') ||
is(bo, 'bpmn:BoundaryEvent') ||
(
is(bo, 'bpmn:SubProcess') && bo.get('triggeredByEvent')
)
)
);
};
/**
* Returns 'true' if the given element supports output parameters
*
* @param {djs.model.Base} element
* @param {boolean} insideConnector
*
* @return {boolean} a boolean value
*/
InputOutputHelper.areOutputParametersSupported = function(element, insideConnector) {
var bo = getBusinessObject(element);
return insideConnector || (!is(bo, 'bpmn:EndEvent') && !bo.loopCharacteristics);
};

@ -0,0 +1,36 @@
'use strict';
var is = require('bpmn-js/lib/util/ModelUtil').is,
getBusinessObject = require('bpmn-js/lib/util/ModelUtil').getBusinessObject,
cmdHelper = require('./CmdHelper');
var ParticipantHelper = {};
module.exports = ParticipantHelper;
ParticipantHelper.modifyProcessBusinessObject = function(element, property, values) {
if (!is(element, 'bpmn:Participant')) {
return {};
}
var bo = getBusinessObject(element).get('processRef'),
properties = {};
properties[property] = values[property];
return cmdHelper.updateBusinessObject(element, bo, properties);
};
ParticipantHelper.getProcessBusinessObject = function(element, propertyName) {
if (!is(element, 'bpmn:Participant')) {
return {};
}
var bo = getBusinessObject(element).get('processRef'),
properties = {};
properties[propertyName] = bo.get(propertyName);
return properties;
};

@ -0,0 +1,8 @@
module.exports = {
__depends__: [
require('./index'),
require('diagram-js/lib/i18n/translate').default
],
__init__: [ 'propertiesPanel' ],
propertiesPanel: [ 'type', require('./PropertiesPanel') ]
};

@ -0,0 +1,103 @@
'use strict';
var domQuery = require('min-dom').query,
domClasses = require('min-dom').classes,
domify = require('min-dom').domify,
bind = require('lodash/bind');
/**
* @class
* @constructor
*/
function Popup(options) {
options = options || {};
this.template = options.template || this.template;
var el = this.el = domify(this.template);
this.header = domQuery('.popup-header', el);
this.body = domQuery('.popup-body', el);
this.footer = domQuery('.popup-footer', el);
document.body.appendChild(el);
this._attachEvents();
}
Popup.prototype.template = '<div class="bpp-properties-panel-popup">' +
'<div class="underlay"></div>' +
'<div class="popup">' +
'<button class="popup-close"><span>Close</span></button>' +
'<div class="popup-header"></div>' +
'<div class="popup-body"></div>' +
'<div class="popup-footer"></div>' +
'</div>' +
'</div>';
Popup.prototype._attachEvents = function() {
var self = this;
var events = this.events;
var el = this.el;
Object.keys(events).forEach(function(instruction) {
var cb = bind(self[events[instruction]], self);
var parts = instruction.split(' ');
var evtName = parts.shift();
var target = parts.length ? parts.shift() : false;
target = target ? domQuery(target, el) : el;
if (!target) { return; }
target.addEventListener(evtName, cb);
});
};
Popup.prototype._detachEvents = function() {
var self = this;
var events = this.events;
var el = this.el;
Object.keys(events).forEach(function(instruction) {
var cb = bind(self[events[instruction]], self);
var parts = instruction.split(' ');
var evtName = parts.shift();
var target = parts.length ? parts.shift() : false;
target = target ? domQuery(target, el) : el;
if (!target) { return; }
target.removeEventListener(evtName, cb);
});
};
Popup.prototype.events = {
// 'keydown:esc': '_handleClose',
'click .underlay': '_handleClose',
'click .popup-close': '_handleClose'
};
Popup.prototype._handleClose = function(evt) {
this.close();
};
Popup.prototype.open = function(content) {
domClasses(this.el).add('open');
};
Popup.prototype.close = function() {
domClasses(this.el).remove('open');
};
Popup.prototype.remove = function() {
this._detachEvents();
if (document.body.contains(this.el)) {
document.body.removeChild(this.el);
}
};
var popup;
module.exports = function() {
if (!popup) {
popup = new Popup();
}
return popup;
};

@ -0,0 +1,542 @@
'use strict';
var inherits = require('inherits');
var PropertiesActivator = require('../../PropertiesActivator');
var asyncCapableHelper = require('../../helper/AsyncCapableHelper'),
ImplementationTypeHelper = require('../../helper/ImplementationTypeHelper');
var is = require('bpmn-js/lib/util/ModelUtil').is;
// bpmn properties
var processProps = require('../../provider/bpmn/parts/ProcessProps'),
eventProps = require('../../provider/bpmn/parts/EventProps'),
linkProps = require('../../provider/bpmn/parts/LinkProps'),
documentationProps = require('../../provider/bpmn/parts/DocumentationProps'),
idProps = require('../../provider/bpmn/parts/IdProps'),
nameProps = require('../../provider/bpmn/parts/NameProps'),
executableProps = require('../../provider/bpmn/parts/ExecutableProps');
// activiti properties
var serviceTaskDelegateProps = require('./parts/ServiceTaskDelegateProps'),
userTaskProps = require('./parts/UserTaskProps'),
asynchronousContinuationProps = require('./parts/AsynchronousContinuationProps'),
callActivityProps = require('./parts/CallActivityProps'),
multiInstanceProps = require('./parts/MultiInstanceLoopProps'),
conditionalProps = require('./parts/ConditionalProps'),
scriptProps = require('./parts/ScriptTaskProps'),
errorProps = require('./parts/ErrorEventProps'),
formProps = require('./parts/FormProps'),
startEventInitiator = require('./parts/StartEventInitiator'),
variableMapping = require('./parts/VariableMappingProps'),
versionTag = require('./parts/VersionTagProps');
var listenerProps = require('./parts/ListenerProps'),
listenerDetails = require('./parts/ListenerDetailProps'),
listenerFields = require('./parts/ListenerFieldInjectionProps');
var elementTemplateChooserProps = require('./element-templates/parts/ChooserProps'),
elementTemplateCustomProps = require('./element-templates/parts/CustomProps');
// Input/Output
var inputOutput = require('./parts/InputOutputProps'),
inputOutputParameter = require('./parts/InputOutputParameterProps');
// Connector
var connectorDetails = require('./parts/ConnectorDetailProps'),
connectorInputOutput = require('./parts/ConnectorInputOutputProps'),
connectorInputOutputParameter = require('./parts/ConnectorInputOutputParameterProps');
// properties
var properties = require('./parts/PropertiesProps');
// job configuration
var jobConfiguration = require('./parts/JobConfigurationProps');
// history time to live
var historyTimeToLive = require('./parts/HistoryTimeToLiveProps');
// candidate starter groups/users
var candidateStarter = require('./parts/CandidateStarterProps');
// tasklist
var tasklist = require('./parts/TasklistProps');
// external task configuration
var externalTaskConfiguration = require('./parts/ExternalTaskConfigurationProps');
// field injection
var fieldInjections = require('./parts/FieldInjectionProps');
var getBusinessObject = require('bpmn-js/lib/util/ModelUtil').getBusinessObject,
eventDefinitionHelper = require('../../helper/EventDefinitionHelper'),
implementationTypeHelper = require('../../helper/ImplementationTypeHelper');
// helpers ////////////////////////////////////////
var isExternalTaskPriorityEnabled = function(element) {
var businessObject = getBusinessObject(element);
// show only if element is a process, a participant ...
if (is(element, 'bpmn:Process') || is(element, 'bpmn:Participant') && businessObject.get('processRef')) {
return true;
}
var externalBo = ImplementationTypeHelper.getServiceTaskLikeBusinessObject(element),
isExternalTask = ImplementationTypeHelper.getImplementationType(externalBo) === 'external';
// ... or an external task with selected external implementation type
return !!ImplementationTypeHelper.isExternalCapable(externalBo) && isExternalTask;
};
var isJobConfigEnabled = function(element) {
var businessObject = getBusinessObject(element);
if (is(element, 'bpmn:Process') || is(element, 'bpmn:Participant') && businessObject.get('processRef')) {
return true;
}
// async behavior
var bo = getBusinessObject(element);
if (asyncCapableHelper.isAsyncBefore(bo) || asyncCapableHelper.isAsyncAfter(bo)) {
return true;
}
// timer definition
if (is(element, 'bpmn:Event')) {
return !!eventDefinitionHelper.getTimerEventDefinition(element);
}
return false;
};
var getInputOutputParameterLabel = function(param, translate) {
if (is(param, 'activiti:InputParameter')) {
return translate('Input Parameter');
}
if (is(param, 'activiti:OutputParameter')) {
return translate('Output Parameter');
}
return '';
};
var getListenerLabel = function(param, translate) {
if (is(param, 'activiti:ExecutionListener')) {
return translate('Execution Listener');
}
if (is(param, 'activiti:TaskListener')) {
return translate('Task Listener');
}
return '';
};
var PROCESS_KEY_HINT = 'This maps to the process definition key.';
var TASK_KEY_HINT = 'This maps to the task definition key.';
function createGeneralTabGroups(
element, canvas, bpmnFactory,
elementRegistry, elementTemplates, translate) {
// refer to target element for external labels
element = element.labelTarget || element;
var generalGroup = {
id: 'general',
label: translate('General'),
entries: []
};
var idOptions;
var processOptions;
if (is(element, 'bpmn:Process')) {
idOptions = { description: PROCESS_KEY_HINT };
}
if (is(element, 'bpmn:UserTask')) {
idOptions = { description: TASK_KEY_HINT };
}
if (is(element, 'bpmn:Participant')) {
processOptions = { processIdDescription: PROCESS_KEY_HINT };
}
idProps(generalGroup, element, translate, idOptions);
nameProps(generalGroup, element, bpmnFactory, canvas, translate);
processProps(generalGroup, element, translate, processOptions);
versionTag(generalGroup, element, translate);
executableProps(generalGroup, element, translate);
elementTemplateChooserProps(generalGroup, element, elementTemplates, translate);
var customFieldsGroups = elementTemplateCustomProps(element, elementTemplates, bpmnFactory, translate);
var detailsGroup = {
id: 'details',
label: translate('Details'),
entries: []
};
serviceTaskDelegateProps(detailsGroup, element, bpmnFactory, translate);
userTaskProps(detailsGroup, element, translate);
scriptProps(detailsGroup, element, bpmnFactory, translate);
linkProps(detailsGroup, element, translate);
callActivityProps(detailsGroup, element, bpmnFactory, translate);
eventProps(detailsGroup, element, bpmnFactory, elementRegistry, translate);
errorProps(detailsGroup, element, bpmnFactory, translate);
conditionalProps(detailsGroup, element, bpmnFactory, translate);
startEventInitiator(detailsGroup, element, translate); // this must be the last element of the details group!
var multiInstanceGroup = {
id: 'multiInstance',
label: translate('Multi Instance'),
entries: []
};
multiInstanceProps(multiInstanceGroup, element, bpmnFactory, translate);
var asyncGroup = {
id : 'async',
label: translate('Asynchronous Continuations'),
entries : []
};
asynchronousContinuationProps(asyncGroup, element, bpmnFactory, translate);
var jobConfigurationGroup = {
id : 'jobConfiguration',
label : translate('Job Configuration'),
entries : [],
enabled: isJobConfigEnabled
};
jobConfiguration(jobConfigurationGroup, element, bpmnFactory, translate);
var externalTaskGroup = {
id : 'externalTaskConfiguration',
label : translate('External Task Configuration'),
entries : [],
enabled: isExternalTaskPriorityEnabled
};
externalTaskConfiguration(externalTaskGroup, element, bpmnFactory, translate);
var candidateStarterGroup = {
id: 'candidateStarterConfiguration',
label: translate('Candidate Starter Configuration'),
entries: []
};
candidateStarter(candidateStarterGroup, element, bpmnFactory, translate);
var historyTimeToLiveGroup = {
id: 'historyConfiguration',
label: translate('History Configuration'),
entries: []
};
historyTimeToLive(historyTimeToLiveGroup, element, bpmnFactory, translate);
var tasklistGroup = {
id: 'tasklist',
label: translate('Tasklist Configuration'),
entries: []
};
tasklist(tasklistGroup, element, bpmnFactory, translate);
var documentationGroup = {
id: 'documentation',
label: translate('Documentation'),
entries: []
};
documentationProps(documentationGroup, element, bpmnFactory, translate);
var groups = [];
groups.push(generalGroup);
customFieldsGroups.forEach(function(group) {
groups.push(group);
});
groups.push(detailsGroup);
groups.push(externalTaskGroup);
groups.push(multiInstanceGroup);
groups.push(asyncGroup);
groups.push(jobConfigurationGroup);
groups.push(candidateStarterGroup);
groups.push(historyTimeToLiveGroup);
groups.push(tasklistGroup);
groups.push(documentationGroup);
return groups;
}
function createVariablesTabGroups(element, bpmnFactory, elementRegistry, translate) {
var variablesGroup = {
id : 'variables',
label : translate('Variables'),
entries: []
};
variableMapping(variablesGroup, element, bpmnFactory, translate);
return [
variablesGroup
];
}
function createFormsTabGroups(element, bpmnFactory, elementRegistry, translate) {
var formGroup = {
id : 'forms',
label : translate('Forms'),
entries: []
};
formProps(formGroup, element, bpmnFactory, translate);
return [
formGroup
];
}
function createListenersTabGroups(element, bpmnFactory, elementRegistry, translate) {
var listenersGroup = {
id : 'listeners',
label: translate('Listeners'),
entries: []
};
var options = listenerProps(listenersGroup, element, bpmnFactory, translate);
var listenerDetailsGroup = {
id: 'listener-details',
entries: [],
enabled: function(element, node) {
return options.getSelectedListener(element, node);
},
label: function(element, node) {
var param = options.getSelectedListener(element, node);
return getListenerLabel(param, translate);
}
};
listenerDetails(listenerDetailsGroup, element, bpmnFactory, options, translate);
var listenerFieldsGroup = {
id: 'listener-fields',
label: translate('Field Injection'),
entries: [],
enabled: function(element, node) {
return options.getSelectedListener(element, node);
}
};
listenerFields(listenerFieldsGroup, element, bpmnFactory, options, translate);
return [
listenersGroup,
listenerDetailsGroup,
listenerFieldsGroup
];
}
function createInputOutputTabGroups(element, bpmnFactory, elementRegistry, translate) {
var inputOutputGroup = {
id: 'input-output',
label: translate('Parameters'),
entries: []
};
var options = inputOutput(inputOutputGroup, element, bpmnFactory, translate);
var inputOutputParameterGroup = {
id: 'input-output-parameter',
entries: [],
enabled: function(element, node) {
return options.getSelectedParameter(element, node);
},
label: function(element, node) {
var param = options.getSelectedParameter(element, node);
return getInputOutputParameterLabel(param, translate);
}
};
inputOutputParameter(inputOutputParameterGroup, element, bpmnFactory, options, translate);
return [
inputOutputGroup,
inputOutputParameterGroup
];
}
function createConnectorTabGroups(element, bpmnFactory, elementRegistry, translate) {
var connectorDetailsGroup = {
id: 'connector-details',
label: translate('Details'),
entries: []
};
connectorDetails(connectorDetailsGroup, element, bpmnFactory, translate);
var connectorInputOutputGroup = {
id: 'connector-input-output',
label: translate('Input/Output'),
entries: []
};
var options = connectorInputOutput(connectorInputOutputGroup, element, bpmnFactory, translate);
var connectorInputOutputParameterGroup = {
id: 'connector-input-output-parameter',
entries: [],
enabled: function(element, node) {
return options.getSelectedParameter(element, node);
},
label: function(element, node) {
var param = options.getSelectedParameter(element, node);
return getInputOutputParameterLabel(param, translate);
}
};
connectorInputOutputParameter(connectorInputOutputParameterGroup, element, bpmnFactory, options, translate);
return [
connectorDetailsGroup,
connectorInputOutputGroup,
connectorInputOutputParameterGroup
];
}
function createFieldInjectionsTabGroups(element, bpmnFactory, elementRegistry, translate) {
var fieldGroup = {
id: 'field-injections-properties',
label: translate('Field Injections'),
entries: []
};
fieldInjections(fieldGroup, element, bpmnFactory, translate);
return [
fieldGroup
];
}
function createExtensionElementsGroups(element, bpmnFactory, elementRegistry, translate) {
var propertiesGroup = {
id : 'extensionElements-properties',
label: translate('Properties'),
entries: []
};
properties(propertiesGroup, element, bpmnFactory, translate);
return [
propertiesGroup
];
}
// activiti Properties Provider /////////////////////////////////////
/**
* A properties provider for activiti related properties.
*
* @param {EventBus} eventBus
* @param {Canvas} canvas
* @param {BpmnFactory} bpmnFactory
* @param {ElementRegistry} elementRegistry
* @param {ElementTemplates} elementTemplates
* @param {Translate} translate
*/
function ActivitiPropertiesProvider(
eventBus, canvas, bpmnFactory,
elementRegistry, elementTemplates, translate) {
PropertiesActivator.call(this, eventBus);
this.getTabs = function(element) {
var generalTab = {
id: 'general',
label: translate('General'),
groups: createGeneralTabGroups(
element, canvas, bpmnFactory,
elementRegistry, elementTemplates, translate)
};
var variablesTab = {
id: 'variables',
label: translate('Variables'),
groups: createVariablesTabGroups(element, bpmnFactory, elementRegistry, translate)
};
var formsTab = {
id: 'forms',
label: translate('Forms'),
groups: createFormsTabGroups(element, bpmnFactory, elementRegistry, translate)
};
var listenersTab = {
id: 'listeners',
label: translate('Listeners'),
groups: createListenersTabGroups(element, bpmnFactory, elementRegistry, translate),
enabled: function(element) {
return !eventDefinitionHelper.getLinkEventDefinition(element)
|| (!is(element, 'bpmn:IntermediateThrowEvent')
&& eventDefinitionHelper.getLinkEventDefinition(element));
}
};
var inputOutputTab = {
id: 'input-output',
label: translate('Input/Output'),
groups: createInputOutputTabGroups(element, bpmnFactory, elementRegistry, translate)
};
var connectorTab = {
id: 'connector',
label: translate('Connector'),
groups: createConnectorTabGroups(element, bpmnFactory, elementRegistry, translate),
enabled: function(element) {
var bo = implementationTypeHelper.getServiceTaskLikeBusinessObject(element);
return bo && implementationTypeHelper.getImplementationType(bo) === 'connector';
}
};
var fieldInjectionsTab = {
id: 'field-injections',
label: translate('Field Injections'),
groups: createFieldInjectionsTabGroups(element, bpmnFactory, elementRegistry, translate)
};
var extensionsTab = {
id: 'extensionElements',
label: translate('Extensions'),
groups: createExtensionElementsGroups(element, bpmnFactory, elementRegistry, translate)
};
return [
generalTab,
variablesTab,
connectorTab,
formsTab,
listenersTab,
inputOutputTab,
fieldInjectionsTab,
extensionsTab
];
};
}
ActivitiPropertiesProvider.$inject = [
'eventBus',
'canvas',
'bpmnFactory',
'elementRegistry',
'elementTemplates',
'translate'
];
inherits(ActivitiPropertiesProvider, PropertiesActivator);
module.exports = ActivitiPropertiesProvider;

@ -0,0 +1,251 @@
'use strict';
var assign = require('lodash/assign');
/**
* Create an input parameter representing the given
* binding and value.
*
* @param {PropertyBinding} binding
* @param {String} value
* @param {BpmnFactory} bpmnFactory
*
* @return {ModdleElement}
*/
function createInputParameter(binding, value, bpmnFactory) {
var scriptFormat = binding.scriptFormat,
parameterValue,
parameterDefinition;
if (scriptFormat) {
parameterDefinition = bpmnFactory.create('activiti:Script', {
scriptFormat: scriptFormat,
value: value
});
} else {
parameterValue = value;
}
return bpmnFactory.create('activiti:InputParameter', {
name: binding.name,
value: parameterValue,
definition: parameterDefinition
});
}
module.exports.createInputParameter = createInputParameter;
/**
* Create an output parameter representing the given
* binding and value.
*
* @param {PropertyBinding} binding
* @param {String} value
* @param {BpmnFactory} bpmnFactory
*
* @return {ModdleElement}
*/
function createOutputParameter(binding, value, bpmnFactory) {
var scriptFormat = binding.scriptFormat,
parameterValue,
parameterDefinition;
if (scriptFormat) {
parameterDefinition = bpmnFactory.create('activiti:Script', {
scriptFormat: scriptFormat,
value: binding.source
});
} else {
parameterValue = binding.source;
}
return bpmnFactory.create('activiti:OutputParameter', {
name: value,
value: parameterValue,
definition: parameterDefinition
});
}
module.exports.createOutputParameter = createOutputParameter;
/**
* Create activiti property from the given binding.
*
* @param {PropertyBinding} binding
* @param {String} value
* @param {BpmnFactory} bpmnFactory
*
* @return {ModdleElement}
*/
function createActivitiProperty(binding, value, bpmnFactory) {
return bpmnFactory.create('activiti:Property', {
name: binding.name,
value: value || ''
});
}
module.exports.createActivitiProperty = createActivitiProperty;
/**
* Create activiti:in element from given binding.
*
* @param {PropertyBinding} binding
* @param {String} value
* @param {BpmnFactory} bpmnFactory
*
* @return {ModdleElement}
*/
function createActivitiIn(binding, value, bpmnFactory) {
var properties = createActivitiInOutAttrs(binding, value);
return bpmnFactory.create('activiti:In', properties);
}
module.exports.createActivitiIn = createActivitiIn;
/**
* Create activiti:in with businessKey element from given binding.
*
* @param {PropertyBinding} binding
* @param {String} value
* @param {BpmnFactory} bpmnFactory
*
* @return {ModdleElement}
*/
function createActivitiInWithBusinessKey(binding, value, bpmnFactory) {
return bpmnFactory.create('activiti:In', {
businessKey: value
});
}
module.exports.createActivitiInWithBusinessKey = createActivitiInWithBusinessKey;
/**
* Create activiti:out element from given binding.
*
* @param {PropertyBinding} binding
* @param {String} value
* @param {BpmnFactory} bpmnFactory
*
* @return {ModdleElement}
*/
function createActivitiOut(binding, value, bpmnFactory) {
var properties = createActivitiInOutAttrs(binding, value);
return bpmnFactory.create('activiti:Out', properties);
}
module.exports.createActivitiOut = createActivitiOut;
/**
* Create activiti:executionListener element containing an inline script from given binding.
*
* @param {PropertyBinding} binding
* @param {String} value
* @param {BpmnFactory} bpmnFactory
*
* @return {ModdleElement}
*/
function createActivitiExecutionListenerScript(binding, value, bpmnFactory) {
var scriptFormat = binding.scriptFormat,
parameterValue,
parameterDefinition;
if (scriptFormat) {
parameterDefinition = bpmnFactory.create('activiti:Script', {
scriptFormat: scriptFormat,
value: value
});
} else {
parameterValue = value;
}
return bpmnFactory.create('activiti:ExecutionListener', {
event: binding.event,
value: parameterValue,
script: parameterDefinition
});
}
module.exports.createActivitiExecutionListenerScript = createActivitiExecutionListenerScript;
/**
* Create activiti:field element containing string or expression from given binding.
*
* @param {PropertyBinding} binding
* @param {String} value
* @param {BpmnFactory} bpmnFactory
*
* @return {ModdleElement}
*/
function createActivitiFieldInjection(binding, value, bpmnFactory) {
var DEFAULT_PROPS = {
'string': undefined,
'expression': undefined,
'name': undefined
};
var props = assign({}, DEFAULT_PROPS);
if (!binding.expression) {
props.string = value;
} else {
props.expression = value;
}
props.name = binding.name;
return bpmnFactory.create('activiti:Field', props);
}
module.exports.createActivitiFieldInjection = createActivitiFieldInjection;
// helpers ////////////////////////////
/**
* Create properties for activiti:in and activiti:out types.
*/
function createActivitiInOutAttrs(binding, value) {
var properties = {};
// activiti:in source(Expression) target
if (binding.target) {
properties.target = binding.target;
if (binding.expression) {
properties.sourceExpression = value;
} else {
properties.source = value;
}
} else
// activiti:(in|out) variables local
if (binding.variables) {
properties.variables = 'all';
if (binding.variables === 'local') {
properties.local = true;
}
}
// activiti:out source(Expression) target
else {
properties.target = value;
[ 'source', 'sourceExpression' ].forEach(function(k) {
if (binding[k]) {
properties[k] = binding[k];
}
});
}
return properties;
}

@ -0,0 +1,132 @@
'use strict';
var inherits = require('inherits');
var getTemplate = require('./Helper').getTemplate;
var PropertiesActivator = require('../../../PropertiesActivator');
var HIGHER_PRIORITY = 1100;
/**
* Applies template visibility settings.
*
* Controlled using `entriesVisible` on template config object:
*
* ```json
* "entriesVisible": {
* "_all": true|false,
* "entryName": true|false,
* ...
* }
* ```
*
* @param {EventBus} eventBus
* @param {ElementTemplates} elementTemplates
*/
function CustomElementsPropertiesActivator(eventBus, elementTemplates) {
PropertiesActivator.call(this, eventBus, HIGHER_PRIORITY);
this.isEntryVisible = function(entry, element) {
var template = getTemplate(element, elementTemplates);
if (template && !isEntryVisible(entry, template)) {
return false;
}
};
this.isPropertyEditable = function(entry, propertyName, element) {
var template = getTemplate(element, elementTemplates);
if (template && !isEntryEditable(entry, template)) {
return false;
}
};
}
CustomElementsPropertiesActivator.$inject = [ 'eventBus', 'elementTemplates' ];
inherits(CustomElementsPropertiesActivator, PropertiesActivator);
module.exports = CustomElementsPropertiesActivator;
// helpers ////////////////////////////////////
var CUSTOM_PROPERTIES_PATTERN = /^custom-/;
var DEFAULT_ENTRIES_VISIBLE = {
_all: false,
id: true,
name: true
};
function isCustomEntry(entry) {
return CUSTOM_PROPERTIES_PATTERN.test(entry.id);
}
function isEntryVisible(entry, template) {
var entryId = entry.id;
if (entryId === 'elementTemplate-chooser' || isCustomEntry(entry)) {
return true;
}
var entriesVisible = template.entriesVisible || DEFAULT_ENTRIES_VISIBLE;
if (typeof entriesVisible === 'boolean') {
return entriesVisible;
}
var defaultVisible = entriesVisible._all || false,
entryVisible = entriesVisible[entryId];
// d = true, e = false => false
// d = false, e = true => true
// d = false, e = false
return (
(defaultVisible === true && entryVisible !== false) ||
(defaultVisible === false && entryVisible === true)
);
}
function isEntryEditable(entry, template) {
var property;
if (isCustomEntry(entry)) {
property = getProperty(template, entry);
return property && property.editable !== false;
}
return true;
}
function getProperty(template, entry) {
var index;
var idx = entry.id.replace('custom-' + template.id + '-', '');
if (idx.indexOf('-') !== -1) {
var indexes = idx.split('-');
if (indexes.length == 2) {
var scopeName = indexes[0].replace(/_/g, ':');
index = parseInt(indexes[1], 10);
if (scopeName && !isNaN(index)) {
return template.scopes[scopeName].properties[index];
}
}
} else {
index = parseInt(idx, 10);
if (!isNaN(index)) {
return template.properties[index];
}
}
throw new Error('cannot extract property index for entry <' + entry.id + '>');
}

@ -0,0 +1,56 @@
'use strict';
var values = require('lodash/values');
/**
* The guy knowing all configured element templates.
*
* This registry won't validate. Use the {@link Validator}
* to verify a template is valid prior to adding it to
* this registry.
*/
function ElementTemplates() {
this._templates = {};
/**
* Sets the known element templates.
*
* @param {Array<TemplateDescriptor>} descriptors
*
* @return {ElementTemplates}
*/
this.set = function(descriptors) {
var templates = this._templates = {};
descriptors.forEach(function(descriptor) {
templates[descriptor.id] = descriptor;
});
return this;
};
/**
* Get template descriptor with given id.
*
* @param {String} id
*
* @return {TemplateDescriptor}
*/
this.get = function(id) {
return this._templates[id];
};
/**
* Return all known template descriptors.
*
* @return {Array<TemplateDescriptor>}
*/
this.getAll = function() {
return values(this._templates);
};
}
module.exports = ElementTemplates;

@ -0,0 +1,96 @@
'use strict';
var Validator = require('./Validator');
/**
* The guy responsible for template loading.
*
* Provide the actual templates via the `config.elementTemplates`.
*
* That configuration can either be an array of template
* descriptors or a node style callback to retrieve
* the templates asynchronously.
*
* @param {Array<TemplateDescriptor>|Function} loadTemplates
* @param {EventBus} eventBus
* @param {ElementTemplates} elementTemplates
*/
function ElementTemplatesLoader(loadTemplates, eventBus, elementTemplates) {
this._loadTemplates = loadTemplates;
this._eventBus = eventBus;
this._elementTemplates = elementTemplates;
var self = this;
eventBus.on('diagram.init', function() {
self.reload();
});
}
module.exports = ElementTemplatesLoader;
ElementTemplatesLoader.$inject = [
'config.elementTemplates',
'eventBus',
'elementTemplates'
];
ElementTemplatesLoader.prototype.reload = function() {
var self = this;
var loadTemplates = this._loadTemplates;
// no templates specified
if (typeof loadTemplates === 'undefined') {
return;
}
// template loader function specified
if (typeof loadTemplates === 'function') {
return loadTemplates(function(err, templates) {
if (err) {
return self.templateErrors([ err ]);
}
self.setTemplates(templates);
});
}
// templates array specified
if (loadTemplates.length) {
return this.setTemplates(loadTemplates);
}
};
ElementTemplatesLoader.prototype.setTemplates = function(templates) {
var elementTemplates = this._elementTemplates;
var validator = new Validator().addAll(templates);
var errors = validator.getErrors(),
validTemplates = validator.getValidTemplates();
elementTemplates.set(validTemplates);
if (errors.length) {
this.templateErrors(errors);
}
this.templatesChanged();
};
ElementTemplatesLoader.prototype.templatesChanged = function() {
this._eventBus.fire('elementTemplates.changed');
};
ElementTemplatesLoader.prototype.templateErrors = function(errors) {
this._eventBus.fire('elementTemplates.errors', {
errors: errors
});
};

@ -0,0 +1,239 @@
'use strict';
var getBusinessObject = require('bpmn-js/lib/util/ModelUtil').getBusinessObject;
var is = require('bpmn-js/lib/util/ModelUtil').is,
isAny = require('bpmn-js/lib/features/modeling/util/ModelingUtil').isAny;
var find = require('lodash/find');
var TEMPLATE_ATTR = 'activiti:modelerTemplate';
/**
* The BPMN 2.0 extension attribute name under
* which the element template is stored.
*
* @type {String}
*/
module.exports.TEMPLATE_ATTR = TEMPLATE_ATTR;
/**
* Get template id for a given diagram element.
*
* @param {djs.model.Base} element
*
* @return {String}
*/
function getTemplateId(element) {
var bo = getBusinessObject(element);
if (bo) {
return bo.get(TEMPLATE_ATTR);
}
}
module.exports.getTemplateId = getTemplateId;
/**
* Get template of a given element.
*
* @param {Element} element
* @param {ElementTemplates} elementTemplates
*
* @return {TemplateDefinition}
*/
function getTemplate(element, elementTemplates) {
var id = getTemplateId(element);
return id && elementTemplates.get(id);
}
module.exports.getTemplate = getTemplate;
/**
* Get default template for a given element.
*
* @param {Element} element
* @param {ElementTemplates} elementTemplates
*
* @return {TemplateDefinition}
*/
function getDefaultTemplate(element, elementTemplates) {
// return first default template, if any exists
return (
elementTemplates.getAll().filter(function(t) {
return isAny(element, t.appliesTo) && t.isDefault;
})
)[0];
}
module.exports.getDefaultTemplate = getDefaultTemplate;
/**
* Find extension with given type in
* BPMN element, diagram element or ExtensionElement.
*
* @param {ModdleElement|djs.model.Base} element
* @param {String} type
*
* @return {ModdleElement} the extension
*/
function findExtension(element, type) {
var bo = getBusinessObject(element);
var extensionElements;
if (is(bo, 'bpmn:ExtensionElements')) {
extensionElements = bo;
} else {
extensionElements = bo.extensionElements;
}
if (!extensionElements) {
return null;
}
return find(extensionElements.get('values'), function(e) {
return is(e, type);
});
}
module.exports.findExtension = findExtension;
function findExtensions(element, types) {
var extensionElements = getExtensionElements(element);
if (!extensionElements) {
return [];
}
return extensionElements.get('values').filter(function(e) {
return isAny(e, types);
});
}
module.exports.findExtensions = findExtensions;
function findActivitiInOut(element, binding) {
var extensionElements = getExtensionElements(element);
if (!extensionElements) {
return;
}
var matcher;
if (binding.type === 'activiti:in') {
matcher = function(e) {
return is(e, 'activiti:In') && isInOut(e, binding);
};
} else
if (binding.type === 'activiti:out') {
matcher = function(e) {
return is(e, 'activiti:Out') && isInOut(e, binding);
};
} else
if (binding.type === 'activiti:in:businessKey') {
matcher = function(e) {
return is(e, 'activiti:In') && 'businessKey' in e;
};
}
return find(extensionElements.get('values'), matcher);
}
module.exports.findActivitiInOut = findActivitiInOut;
function findActivitiProperty(activitiProperties, binding) {
return find(activitiProperties.get('values'), function(p) {
return p.name === binding.name;
});
}
module.exports.findActivitiProperty = findActivitiProperty;
function findInputParameter(inputOutput, binding) {
var parameters = inputOutput.get('inputParameters');
return find(parameters, function(p) {
return p.name === binding.name;
});
}
module.exports.findInputParameter = findInputParameter;
function findOutputParameter(inputOutput, binding) {
var parameters = inputOutput.get('outputParameters');
return find(parameters, function(p) {
var value = p.value;
if (!binding.scriptFormat) {
return value === binding.source;
}
var definition = p.definition;
if (!definition || binding.scriptFormat !== definition.scriptFormat) {
return false;
}
return definition.value === binding.source;
});
}
module.exports.findOutputParameter = findOutputParameter;
// helpers /////////////////////////////////
function getExtensionElements(element) {
var bo = getBusinessObject(element);
if (is(bo, 'bpmn:ExtensionElements')) {
return bo;
} else {
return bo.extensionElements;
}
}
function isInOut(element, binding) {
if (binding.type === 'activiti:in') {
// find based on target attribute
if (binding.target) {
return element.target === binding.target;
}
}
if (binding.type === 'activiti:out') {
// find based on source / sourceExpression
if (binding.source) {
return element.source === binding.source;
}
if (binding.sourceExpression) {
return element.sourceExpression === binding.sourceExpression;
}
}
// find based variables / local combination
if (binding.variables) {
return element.variables === 'all' && (
binding.variables !== 'local' || element.local
);
}
}

@ -0,0 +1,303 @@
'use strict';
var isArray = require('lodash/isArray');
var isObject = require('lodash/isObject');
var DROPDOWN_TYPE = 'Dropdown';
var VALID_TYPES = [ 'String', 'Text', 'Boolean', 'Hidden', DROPDOWN_TYPE ];
var PROPERTY_TYPE = 'property',
ACTIVITI_PROPERTY_TYPE = 'activiti:property',
ACTIVITI_INPUT_PARAMETER_TYPE = 'activiti:inputParameter',
ACTIVITI_OUTPUT_PARAMETER_TYPE = 'activiti:outputParameter',
ACTIVITI_IN_TYPE = 'activiti:in',
ACTIVITI_OUT_TYPE = 'activiti:out',
ACTIVITI_IN_BUSINESS_KEY_TYPE = 'activiti:in:businessKey',
ACTIVITI_EXECUTION_LISTENER = 'activiti:executionListener',
ACTIVITI_FIELD = 'activiti:field';
var VALID_BINDING_TYPES = [
PROPERTY_TYPE,
ACTIVITI_PROPERTY_TYPE,
ACTIVITI_INPUT_PARAMETER_TYPE,
ACTIVITI_OUTPUT_PARAMETER_TYPE,
ACTIVITI_IN_TYPE,
ACTIVITI_OUT_TYPE,
ACTIVITI_IN_BUSINESS_KEY_TYPE,
ACTIVITI_EXECUTION_LISTENER,
ACTIVITI_FIELD
];
/**
* A element template validator.
*/
function Validator() {
this._templatesById = {};
this._validTemplates = [];
this._errors = [];
/**
* Adds the templates.
*
* @param {Array<TemplateDescriptor>} templates
*
* @return {Validator} self
*/
this.addAll = function(templates) {
if (!isArray(templates)) {
this._logError('templates must be []');
} else {
templates.forEach(this.add, this);
}
return this;
};
/**
* Add the given element template, if it is valid.
*
* @param {TemplateDescriptor} template
*
* @return {Validator} self
*/
this.add = function(template) {
var err = this._validateTemplate(template);
if (!err) {
this._templatesById[template.id] = template;
this._validTemplates.push(template);
}
return this;
};
/**
* Validate given template and return error (if any).
*
* @param {TemplateDescriptor} template
*
* @return {Error} validation error, if any
*/
this._validateTemplate = function(template) {
var err,
id = template.id,
appliesTo = template.appliesTo,
properties = template.properties,
scopes = template.scopes;
if (!id) {
return this._logError('missing template id');
}
if (id in this._templatesById) {
return this._logError('template id <' + id + '> already used');
}
if (!isArray(appliesTo)) {
err = this._logError('missing appliesTo=[]', template);
}
if (!isArray(properties)) {
err = this._logError('missing properties=[]', template);
} else {
if (!this._validateProperties(properties)) {
err = new Error('invalid properties');
}
}
if (scopes) {
err = this._validateScopes(template, scopes);
}
return err;
};
this._validateScopes = function(template, scopes) {
var err,
scope,
scopeName;
if (!isObject(scopes) || isArray(scopes)) {
return this._logError('invalid scopes, should be scopes={}', template);
}
for (scopeName in scopes) {
scope = scopes[scopeName];
if (!isObject(scope) || isArray(scope)) {
err = this._logError('invalid scope, should be scope={}', template);
}
if (!isArray(scope.properties)) {
err = this._logError(
'missing properties=[] in scope <' + scopeName + '>', template
);
} else {
if (!this._validateProperties(scope.properties)) {
err = new Error('invalid properties in scope <' + scopeName + '>');
}
}
}
return err;
};
/**
* Validate properties and return false if any is invalid.
*
* @param {Array<PropertyDescriptor>} properties
*
* @return {Boolean} true if all properties are valid
*/
this._validateProperties = function(properties) {
var validProperties = properties.filter(this._validateProperty, this);
return properties.length === validProperties.length;
};
/**
* Validate property and return false, if there was
* a validation error.
*
* @param {PropertyDescriptor} property
*
* @return {Boolean} true if property is valid
*/
this._validateProperty = function(property) {
var type = property.type,
binding = property.binding;
var err;
var bindingType = binding.type;
if (VALID_TYPES.indexOf(type) === -1) {
err = this._logError(
'invalid property type <' + type + '>; ' +
'must be any of { ' + VALID_TYPES.join(', ') + ' }'
);
}
if (type === DROPDOWN_TYPE && bindingType !== ACTIVITI_EXECUTION_LISTENER) {
if (!isArray(property.choices)) {
err = this._logError(
'must provide choices=[] with ' + DROPDOWN_TYPE + ' type'
);
} else
if (!property.choices.every(isDropdownChoiceValid)) {
err = this._logError(
'{ name, value } must be specified for ' +
DROPDOWN_TYPE + ' choices'
);
}
}
if (!binding) {
return this._logError('property missing binding');
}
if (VALID_BINDING_TYPES.indexOf(bindingType) === -1) {
err = this._logError(
'invalid property.binding type <' + bindingType + '>; ' +
'must be any of { ' + VALID_BINDING_TYPES.join(', ') + ' }'
);
}
if (bindingType === PROPERTY_TYPE ||
bindingType === ACTIVITI_PROPERTY_TYPE ||
bindingType === ACTIVITI_INPUT_PARAMETER_TYPE ||
bindingType === ACTIVITI_FIELD) {
if (!binding.name) {
err = this._logError(
'property.binding <' + bindingType + '> requires name'
);
}
}
if (bindingType === ACTIVITI_OUTPUT_PARAMETER_TYPE) {
if (!binding.source) {
err = this._logError(
'property.binding <' + bindingType + '> requires source'
);
}
}
if (bindingType === ACTIVITI_IN_TYPE) {
if (!binding.variables && !binding.target) {
err = this._logError(
'property.binding <' + bindingType + '> requires ' +
'variables or target'
);
}
}
if (bindingType === ACTIVITI_OUT_TYPE) {
if (!binding.variables && !binding.source && !binding.sourceExpression) {
err = this._logError(
'property.binding <' + bindingType + '> requires ' +
'variables, sourceExpression or source'
);
}
}
if (bindingType === ACTIVITI_EXECUTION_LISTENER) {
if (type !== 'Hidden') {
err = this._logError(
'invalid property type <' + type + '> for ' + ACTIVITI_EXECUTION_LISTENER + '; ' +
'must be <Hidden>'
);
}
}
return !err;
};
this._logError = function(err, template) {
if (typeof err === 'string') {
if (template) {
err = 'template(id: ' + template.id + ') ' + err;
}
err = new Error(err);
}
this._errors.push(err);
return err;
};
this.getErrors = function() {
return this._errors;
};
this.getValidTemplates = function() {
return this._validTemplates;
};
}
module.exports = Validator;
// helpers ///////////////////////////////////
function isDropdownChoiceValid(c) {
return 'name' in c && 'value' in c;
}

@ -0,0 +1,471 @@
'use strict';
var findExtension = require('../Helper').findExtension,
findExtensions = require('../Helper').findExtensions;
var createActivitiProperty = require('../CreateHelper').createActivitiProperty,
createInputParameter = require('../CreateHelper').createInputParameter,
createOutputParameter = require('../CreateHelper').createOutputParameter,
createActivitiIn = require('../CreateHelper').createActivitiIn,
createActivitiOut = require('../CreateHelper').createActivitiOut,
createActivitiInWithBusinessKey = require('../CreateHelper').createActivitiInWithBusinessKey,
createActivitiExecutionListenerScript = require('../CreateHelper').createActivitiExecutionListenerScript,
createActivitiFieldInjection = require('../CreateHelper').createActivitiFieldInjection;
var forEach = require('lodash/forEach');
var ACTIVITI_SERVICE_TASK_LIKE = [
'activiti:class',
'activiti:delegateExpression',
'activiti:expression'
];
/**
* A handler that changes the modeling template of a BPMN element.
*/
function ChangeElementTemplateHandler(modeling, commandStack, bpmnFactory) {
function getOrCreateExtensionElements(element) {
var bo = element.businessObject;
var extensionElements = bo.extensionElements;
// add extension elements
if (!extensionElements) {
extensionElements = bpmnFactory.create('bpmn:ExtensionElements', {
values: []
});
modeling.updateProperties(element, {
extensionElements: extensionElements
});
}
return extensionElements;
}
function updateModelerTemplate(element, newTemplate) {
modeling.updateProperties(element, {
'activiti:modelerTemplate': newTemplate && newTemplate.id
});
}
function updateIoMappings(element, newTemplate, context) {
var newMappings = createInputOutputMappings(newTemplate, bpmnFactory),
oldMappings;
if (!newMappings) {
return;
}
if (context) {
commandStack.execute('properties-panel.update-businessobject', {
element: element,
businessObject: context,
properties: { inputOutput: newMappings }
});
} else {
context = getOrCreateExtensionElements(element);
oldMappings = findExtension(element, 'activiti:InputOutput');
commandStack.execute('properties-panel.update-businessobject-list', {
element: element,
currentObject: context,
propertyName: 'values',
objectsToAdd: [ newMappings ],
objectsToRemove: oldMappings ? [ oldMappings ] : []
});
}
}
function updateActivitiField(element, newTemplate, context) {
var newMappings = createActivitiFieldInjections(newTemplate, bpmnFactory),
oldMappings;
if (!newMappings) {
return;
}
if (context) {
commandStack.execute('properties-panel.update-businessobject', {
element: element,
businessObject: context,
properties: { field: newMappings }
});
} else {
context = getOrCreateExtensionElements(element);
oldMappings = findExtensions(element, ['activiti:Field']);
commandStack.execute('properties-panel.update-businessobject-list', {
element: element,
currentObject: context,
propertyName: 'values',
objectsToAdd: newMappings,
objectsToRemove: oldMappings ? oldMappings : []
});
}
}
function updateActivitiProperties(element, newTemplate, context) {
var newProperties = createActivitiProperties(newTemplate, bpmnFactory),
oldProperties;
if (!newProperties) {
return;
}
if (context) {
commandStack.execute('properties-panel.update-businessobject', {
element: element,
businessObject: context,
properties: { properties: newProperties }
});
} else {
context = getOrCreateExtensionElements(element);
oldProperties = findExtension(element, 'activiti:Properties');
commandStack.execute('properties-panel.update-businessobject-list', {
element: element,
currentObject: context,
propertyName: 'values',
objectsToAdd: [ newProperties ],
objectsToRemove: oldProperties ? [ oldProperties ] : []
});
}
}
function updateProperties(element, newTemplate, context) {
var newProperties = createBpmnPropertyUpdates(newTemplate, bpmnFactory);
var newPropertiesCount = Object.keys(newProperties).length;
if (!newPropertiesCount) {
return;
}
if (context) {
commandStack.execute('properties-panel.update-businessobject', {
element: element,
businessObject: context,
properties: newProperties
});
} else {
modeling.updateProperties(element, newProperties);
}
}
function updateInOut(element, newTemplate, context) {
var newInOut = createActivitiInOut(newTemplate, bpmnFactory),
oldInOut;
if (!newInOut) {
return;
}
if (context) {
commandStack.execute('properties-panel.update-businessobject', {
element: element,
businessObject: context,
properties: { inout: newInOut }
});
} else {
context = getOrCreateExtensionElements(element);
oldInOut = findExtensions(context, [ 'activiti:In', 'activiti:Out' ]);
commandStack.execute('properties-panel.update-businessobject-list', {
element: element,
currentObject: context,
propertyName: 'values',
objectsToAdd: newInOut,
objectsToRemove: oldInOut
});
}
}
function updateExecutionListener(element, newTemplate, context) {
var newExecutionListeners = createActivitiExecutionListeners(newTemplate, bpmnFactory),
oldExecutionsListeners;
if (!newExecutionListeners.length) {
return;
}
if (context) {
commandStack.execute('properties-panel.update-businessobject', {
element: element,
businessObject: context,
properties: { executionListener: newExecutionListeners }
});
} else {
context = getOrCreateExtensionElements(element);
oldExecutionsListeners = findExtensions(context, [ 'activiti:ExecutionListener' ]);
commandStack.execute('properties-panel.update-businessobject-list', {
element: element,
currentObject: context,
propertyName: 'values',
objectsToAdd: newExecutionListeners,
objectsToRemove: oldExecutionsListeners
});
}
}
/**
* Update / recreate a scoped element.
*
* @param {djs.model.Base} element the diagram parent element
* @param {String} scopeName name of the scope, i.e. activiti:Connector
* @param {Object} scopeDefinition
*/
function updateScopeElements(element, scopeName, scopeDefinition) {
var scopeElement = bpmnFactory.create(scopeName);
// update activiti:inputOutput
updateIoMappings(element, scopeDefinition, scopeElement);
// update activiti:field
updateActivitiField(element, scopeDefinition, scopeElement);
// update activiti:properties
updateActivitiProperties(element, scopeDefinition, scopeElement);
// update other properties (bpmn:condition, activiti:async, ...)
updateProperties(element, scopeDefinition, scopeElement);
// update activiti:in and activiti:out
updateInOut(element, scopeDefinition, scopeElement);
// update activiti:executionListener
updateExecutionListener(element, scopeDefinition, scopeElement);
var extensionElements = getOrCreateExtensionElements(element);
var oldScope = findExtension(extensionElements, scopeName);
commandStack.execute('properties-panel.update-businessobject-list', {
element: element,
currentObject: extensionElements,
propertyName: 'values',
objectsToAdd: [ scopeElement ],
objectsToRemove: oldScope ? [ oldScope ] : []
});
}
/**
* Compose an element template change action, updating all
* necessary underlying properties.
*
* @param {Object} context
* @param {Object} context.element
* @param {Object} context.oldTemplate
* @param {Object} context.newTemplate
*/
this.preExecute = function(context) {
var element = context.element,
newTemplate = context.newTemplate;
// update activiti:modelerTemplate attribute
updateModelerTemplate(element, newTemplate);
if (newTemplate) {
// update activiti:inputOutput
updateIoMappings(element, newTemplate);
// update activiti:field
updateActivitiField(element, newTemplate);
// update activiti:properties
updateActivitiProperties(element, newTemplate);
// update other properties (bpmn:condition, activiti:async, ...)
updateProperties(element, newTemplate);
// update activiti:in and activiti:out
updateInOut(element, newTemplate);
// update activiti:executionListener
updateExecutionListener(element, newTemplate);
// loop on scopes properties
forEach(newTemplate.scopes, function(scopeDefinition, scopeName) {
updateScopeElements(element, scopeName, scopeDefinition);
});
}
};
}
ChangeElementTemplateHandler.$inject = [ 'modeling', 'commandStack', 'bpmnFactory' ];
module.exports = ChangeElementTemplateHandler;
// helpers /////////////////////////////
function createBpmnPropertyUpdates(template, bpmnFactory) {
var propertyUpdates = {};
template.properties.forEach(function(p) {
var binding = p.binding,
bindingTarget = binding.name,
propertyValue;
if (binding.type === 'property') {
if (bindingTarget === 'conditionExpression') {
propertyValue = bpmnFactory.create('bpmn:FormalExpression', {
body: p.value,
language: binding.scriptFormat
});
} else {
propertyValue = p.value;
}
// assigning activiti:async to true|false
// assigning bpmn:conditionExpression to { $type: 'bpmn:FormalExpression', ... }
propertyUpdates[bindingTarget] = propertyValue;
// make sure we unset other "implementation types"
// when applying a activiti:class template onto a preconfigured
// activiti:delegateExpression element
if (ACTIVITI_SERVICE_TASK_LIKE.indexOf(bindingTarget) !== -1) {
ACTIVITI_SERVICE_TASK_LIKE.forEach(function(prop) {
if (prop !== bindingTarget) {
propertyUpdates[prop] = undefined;
}
});
}
}
});
return propertyUpdates;
}
function createActivitiFieldInjections(template, bpmnFactory) {
var injections = [];
template.properties.forEach(function(p) {
var binding = p.binding,
bindingType = binding.type;
if (bindingType === 'activiti:field') {
injections.push(createActivitiFieldInjection(
binding, p.value, bpmnFactory
));
}
});
if (injections.length) {
return injections;
}
}
function createActivitiProperties(template, bpmnFactory) {
var properties = [];
template.properties.forEach(function(p) {
var binding = p.binding,
bindingType = binding.type;
if (bindingType === 'activiti:property') {
properties.push(createActivitiProperty(
binding, p.value, bpmnFactory
));
}
});
if (properties.length) {
return bpmnFactory.create('activiti:Properties', {
values: properties
});
}
}
function createInputOutputMappings(template, bpmnFactory) {
var inputParameters = [],
outputParameters = [];
template.properties.forEach(function(p) {
var binding = p.binding,
bindingType = binding.type;
if (bindingType === 'activiti:inputParameter') {
inputParameters.push(createInputParameter(
binding, p.value, bpmnFactory
));
}
if (bindingType === 'activiti:outputParameter') {
outputParameters.push(createOutputParameter(
binding, p.value, bpmnFactory
));
}
});
// do we need to create new ioMappings (?)
if (outputParameters.length || inputParameters.length) {
return bpmnFactory.create('activiti:InputOutput', {
inputParameters: inputParameters,
outputParameters: outputParameters
});
}
}
function createActivitiInOut(template, bpmnFactory) {
var inOuts = [];
template.properties.forEach(function(p) {
var binding = p.binding,
bindingType = binding.type;
if (bindingType === 'activiti:in') {
inOuts.push(createActivitiIn(
binding, p.value, bpmnFactory
));
} else
if (bindingType === 'activiti:out') {
inOuts.push(createActivitiOut(
binding, p.value, bpmnFactory
));
} else
if (bindingType === 'activiti:in:businessKey') {
inOuts.push(createActivitiInWithBusinessKey(
binding, p.value, bpmnFactory
));
}
});
return inOuts;
}
function createActivitiExecutionListeners(template, bpmnFactory) {
var executionListener = [];
template.properties.forEach(function(p) {
var binding = p.binding,
bindingType = binding.type;
if (bindingType === 'activiti:executionListener') {
executionListener.push(createActivitiExecutionListenerScript(
binding, p.value, bpmnFactory
));
}
});
return executionListener;
}

@ -0,0 +1,46 @@
'use strict';
var ChangeElementTemplateHandler = require('./ChangeElementTemplateHandler');
var getTemplate = require('../Helper').getTemplate,
getDefaultTemplate = require('../Helper').getDefaultTemplate;
function registerHandlers(commandStack, elementTemplates, eventBus, elementRegistry) {
commandStack.registerHandler(
'propertiesPanel.activiti.changeTemplate',
ChangeElementTemplateHandler
);
// apply default element templates on shape creation
eventBus.on([ 'commandStack.shape.create.postExecuted' ], function(context) {
applyDefaultTemplate(context.context.shape, elementTemplates, commandStack);
});
// apply default element templates on connection creation
eventBus.on([ 'commandStack.connection.create.postExecuted' ], function(context) {
applyDefaultTemplate(context.context.connection, elementTemplates, commandStack);
});
}
registerHandlers.$inject = [ 'commandStack', 'elementTemplates', 'eventBus', 'elementRegistry' ];
module.exports = {
__init__: [ registerHandlers ]
};
function applyDefaultTemplate(element, elementTemplates, commandStack) {
if (!getTemplate(element, elementTemplates)
&& getDefaultTemplate(element, elementTemplates)) {
var command = 'propertiesPanel.activiti.changeTemplate';
var commandContext = {
element: element,
newTemplate: getDefaultTemplate(element, elementTemplates)
};
commandStack.execute(command, commandContext);
}
}

@ -0,0 +1,13 @@
module.exports = {
__depends__: [
require('./cmd/index'),
require('diagram-js/lib/i18n/translate').default
],
__init__: [
'customElementsPropertiesActivator',
'elementTemplatesLoader'
],
customElementsPropertiesActivator: [ 'type', require('./CustomElementsPropertiesActivator') ],
elementTemplates: [ 'type', require('./ElementTemplates') ],
elementTemplatesLoader: [ 'type', require('./ElementTemplatesLoader') ]
};

@ -0,0 +1,151 @@
'use strict';
var entryFactory = require('../../../../factory/EntryFactory'),
is = require('bpmn-js/lib/util/ModelUtil').is,
getTemplate = require('../Helper').getTemplate,
getTemplateId = require('../Helper').getTemplateId;
var find = require('lodash/find');
var TEMPLATE_ATTR = require('../Helper').TEMPLATE_ATTR;
function isAny(element, types) {
return types.reduce(function(result, type) {
return result || is(element, type);
}, false);
}
module.exports = function(group, element, elementTemplates, translate) {
var options = getTemplateOptions(element, elementTemplates, translate);
if (options.length === 1 && !options[0].isDefault) {
return;
}
// select element template (via dropdown)
group.entries.push(entryFactory.selectBox({
id: 'elementTemplate-chooser',
label: translate('Element Template'),
modelProperty: 'activiti:modelerTemplate',
selectOptions: options,
set: function(element, properties) {
return applyTemplate(element, properties[TEMPLATE_ATTR], elementTemplates);
},
disabled: function() {
var template = getTemplate(element, elementTemplates);
return template && isDefaultTemplate(template);
}
}));
};
// helpers //////////////////////////////////////
function applyTemplate(element, newTemplateId, elementTemplates) {
// cleanup
// clear input output mappings
// undo changes to properties defined in template
// re-establish
// set input output mappings
// apply changes to properties as defined in new template
var oldTemplate = getTemplate(element, elementTemplates),
newTemplate = elementTemplates.get(newTemplateId);
if (oldTemplate === newTemplate) {
return;
}
return {
cmd: 'propertiesPanel.activiti.changeTemplate',
context: {
element: element,
oldTemplate: oldTemplate,
newTemplate: newTemplate
}
};
}
function getTemplateOptions(element, elementTemplates, translate) {
var currentTemplateId = getTemplateId(element);
var emptyOption = {
name: '',
value: ''
};
var allOptions = elementTemplates.getAll().reduce(function(templates, t) {
if (!isAny(element, t.appliesTo)) {
return templates;
}
return templates.concat({
name: translate(t.name),
value: t.id,
isDefault: t.isDefault
});
}, [ emptyOption ]);
var defaultOption = find(allOptions, function(option) {
return isDefaultTemplate(option);
});
var currentOption = find(allOptions, function(option) {
return option.value === currentTemplateId;
});
if (currentTemplateId && !currentOption) {
currentOption = unknownTemplate(currentTemplateId, translate);
allOptions.push(currentOption);
}
if (!defaultOption) {
// return all options, including empty
// and optionally unknownTemplate option
return allOptions;
}
// special limited handling for
// default options
var options = [];
// current template not set
if (!currentTemplateId) {
options.push({
name: '',
value: ''
});
}
// current template not default
if (currentOption && currentOption !== defaultOption) {
options.push(currentOption);
}
options.push(defaultOption);
// [ (empty), (current), defaultOption ]
return options;
}
function unknownTemplate(templateId, translate) {
return {
name: translate('[unknown template: {templateId}]', { templateId: templateId }),
value: templateId
};
}
function isDefaultTemplate(elementTemplate) {
return elementTemplate.isDefault;
}

@ -0,0 +1,770 @@
'use strict';
var assign = require('lodash/assign');
var entryFactory = require('../../../../factory/EntryFactory'),
getBusinessObject = require('bpmn-js/lib/util/ModelUtil').getBusinessObject,
getTemplate = require('../Helper').getTemplate,
cmdHelper = require('../../../../helper/CmdHelper'),
elementHelper = require('../../../../helper/ElementHelper');
var findExtension = require('../Helper').findExtension,
findExtensions = require('../Helper').findExtensions,
findInputParameter = require('../Helper').findInputParameter,
findOutputParameter = require('../Helper').findOutputParameter,
findActivitiProperty = require('../Helper').findActivitiProperty,
findActivitiInOut = require('../Helper').findActivitiInOut;
var createActivitiProperty = require('../CreateHelper').createActivitiProperty,
createInputParameter = require('../CreateHelper').createInputParameter,
createOutputParameter = require('../CreateHelper').createOutputParameter,
createActivitiIn = require('../CreateHelper').createActivitiIn,
createActivitiOut = require('../CreateHelper').createActivitiOut,
createActivitiInWithBusinessKey = require('../CreateHelper').createActivitiInWithBusinessKey,
createActivitiFieldInjection = require('../CreateHelper').createActivitiFieldInjection;
var ACTIVITI_PROPERTY_TYPE = 'activiti:property',
ACTIVITI_INPUT_PARAMETER_TYPE = 'activiti:inputParameter',
ACTIVITI_OUTPUT_PARAMETER_TYPE = 'activiti:outputParameter',
ACTIVITI_IN_TYPE = 'activiti:in',
ACTIVITI_OUT_TYPE = 'activiti:out',
ACTIVITI_IN_BUSINESS_KEY_TYPE = 'activiti:in:businessKey',
ACTIVITI_EXECUTION_LISTENER_TYPE = 'activiti:executionListener',
ACTIVITI_FIELD = 'activiti:field';
var BASIC_MODDLE_TYPES = [
'Boolean',
'Integer',
'String'
];
var EXTENSION_BINDING_TYPES = [
ACTIVITI_PROPERTY_TYPE,
ACTIVITI_INPUT_PARAMETER_TYPE,
ACTIVITI_OUTPUT_PARAMETER_TYPE,
ACTIVITI_IN_TYPE,
ACTIVITI_OUT_TYPE,
ACTIVITI_IN_BUSINESS_KEY_TYPE,
ACTIVITI_FIELD
];
var IO_BINDING_TYPES = [
ACTIVITI_INPUT_PARAMETER_TYPE,
ACTIVITI_OUTPUT_PARAMETER_TYPE
];
var IN_OUT_BINDING_TYPES = [
ACTIVITI_IN_TYPE,
ACTIVITI_OUT_TYPE,
ACTIVITI_IN_BUSINESS_KEY_TYPE
];
/**
* Injects custom properties into the given group.
*
* @param {djs.model.Base} element
* @param {ElementTemplates} elementTemplates
* @param {BpmnFactory} bpmnFactory
* @param {Function} translate
*/
module.exports = function(element, elementTemplates, bpmnFactory, translate) {
var template = getTemplate(element, elementTemplates);
if (!template) {
return [];
}
var renderCustomField = function(id, p, idx) {
var propertyType = p.type;
var entryOptions = {
id: id,
description: p.description,
label: p.label ? translate(p.label) : p.label,
modelProperty: id,
get: propertyGetter(id, p),
set: propertySetter(id, p, bpmnFactory),
validate: propertyValidator(id, p, translate)
};
var entry;
if (propertyType === 'Boolean') {
entry = entryFactory.checkbox(entryOptions);
}
if (propertyType === 'String') {
entry = entryFactory.textField(entryOptions);
}
if (propertyType === 'Text') {
entry = entryFactory.textBox(entryOptions);
}
if (propertyType === 'Dropdown') {
entryOptions.selectOptions = p.choices;
entry = entryFactory.selectBox(entryOptions);
}
return entry;
};
var groups = [];
var id, entry;
var customFieldsGroup = {
id: 'customField',
label: translate('Custom Fields'),
entries: []
};
template.properties.forEach(function(p, idx) {
id = 'custom-' + template.id + '-' + idx;
entry = renderCustomField(id, p, idx);
if (entry) {
customFieldsGroup.entries.push(entry);
}
});
if (customFieldsGroup.entries.length > 0) {
groups.push(customFieldsGroup);
}
if (template.scopes) {
for (var scopeName in template.scopes) {
var scope = template.scopes[scopeName];
var idScopeName = scopeName.replace(/:/g, '_');
var customScopeFieldsGroup = {
id: 'customField-' + idScopeName,
label: translate('Custom Fields for scope: ') + scopeName,
entries: []
};
scope.properties.forEach(function(p, idx) {
var propertyId = 'custom-' + template.id + '-' + idScopeName + '-' + idx;
var scopedProperty = propertyWithScope(p, scopeName);
entry = renderCustomField(propertyId, scopedProperty, idx);
if (entry) {
customScopeFieldsGroup.entries.push(entry);
}
});
if (customScopeFieldsGroup.entries.length > 0) {
groups.push(customScopeFieldsGroup);
}
}
}
return groups;
};
// getters, setters and validators ///////////////
/**
* Return a getter that retrieves the given property.
*
* @param {String} name
* @param {PropertyDescriptor} property
*
* @return {Function}
*/
function propertyGetter(name, property) {
/* getter */
return function get(element) {
var value = getPropertyValue(element, property);
return objectWithKey(name, value);
};
}
/**
* Return a setter that updates the given property.
*
* @param {String} name
* @param {PropertyDescriptor} property
* @param {BpmnFactory} bpmnFactory
*
* @return {Function}
*/
function propertySetter(name, property, bpmnFactory) {
/* setter */
return function set(element, values) {
var value = values[name];
return setPropertyValue(element, property, value, bpmnFactory);
};
}
/**
* Return a validator that ensures the property is ok.
*
* @param {String} name
* @param {PropertyDescriptor} property
* @param {Function} translate
*
* @return {Function}
*/
function propertyValidator(name, property, translate) {
/* validator */
return function validate(element, values) {
var value = values[name];
var error = validateValue(value, property, translate);
if (error) {
return objectWithKey(name, error);
}
};
}
// get, set and validate helpers ///////////////////
/**
* Return the value of the specified property descriptor,
* on the passed diagram element.
*
* @param {djs.model.Base} element
* @param {PropertyDescriptor} property
*
* @return {Any}
*/
function getPropertyValue(element, property) {
var bo = getBusinessObject(element);
var binding = property.binding,
scope = property.scope;
var bindingType = binding.type,
bindingName = binding.name;
var propertyValue = property.value || '';
if (scope) {
bo = findExtension(bo, scope.name);
if (!bo) {
return propertyValue;
}
}
// property
if (bindingType === 'property') {
var value = bo.get(bindingName);
if (bindingName === 'conditionExpression') {
if (value) {
return value.body;
} else {
// return defined default
return propertyValue;
}
} else {
// return value; default to defined default
return typeof value !== 'undefined' ? value : propertyValue;
}
}
var activitiProperties,
activitiProperty;
if (bindingType === ACTIVITI_PROPERTY_TYPE) {
if (scope) {
activitiProperties = bo.get('properties');
} else {
activitiProperties = findExtension(bo, 'activiti:Properties');
}
if (activitiProperties) {
activitiProperty = findActivitiProperty(activitiProperties, binding);
if (activitiProperty) {
return activitiProperty.value;
}
}
return propertyValue;
}
var inputOutput,
ioParameter;
if (IO_BINDING_TYPES.indexOf(bindingType) !== -1) {
if (scope) {
inputOutput = bo.get('inputOutput');
} else {
inputOutput = findExtension(bo, 'activiti:InputOutput');
}
if (!inputOutput) {
// ioParameter cannot exist yet, return property value
return propertyValue;
}
}
// activiti input parameter
if (bindingType === ACTIVITI_INPUT_PARAMETER_TYPE) {
ioParameter = findInputParameter(inputOutput, binding);
if (ioParameter) {
if (binding.scriptFormat) {
if (ioParameter.definition) {
return ioParameter.definition.value;
}
} else {
return ioParameter.value || '';
}
}
return propertyValue;
}
// activiti output parameter
if (binding.type === ACTIVITI_OUTPUT_PARAMETER_TYPE) {
ioParameter = findOutputParameter(inputOutput, binding);
if (ioParameter) {
return ioParameter.name;
}
return propertyValue;
}
var ioElement;
if (IN_OUT_BINDING_TYPES.indexOf(bindingType) != -1) {
ioElement = findActivitiInOut(bo, binding);
if (ioElement) {
if (bindingType === ACTIVITI_IN_BUSINESS_KEY_TYPE) {
return ioElement.businessKey;
} else
if (bindingType === ACTIVITI_OUT_TYPE) {
return ioElement.target;
} else
if (bindingType === ACTIVITI_IN_TYPE) {
return ioElement[binding.expression ? 'sourceExpression' : 'source'];
}
}
return propertyValue;
}
if (bindingType === ACTIVITI_EXECUTION_LISTENER_TYPE) {
var executionListener;
if (scope) {
executionListener = bo.get('executionListener');
} else {
executionListener = findExtension(bo, 'activiti:ExecutionListener');
}
return executionListener.script.value;
}
var fieldInjection;
if (ACTIVITI_FIELD === bindingType) {
var fieldInjections = findExtensions(bo, [ 'activiti:Field' ]);
fieldInjections.forEach(function(item) {
if (item.name === binding.name) {
fieldInjection = item;
}
});
if (fieldInjection) {
return fieldInjection.string || fieldInjection.expression;
} else {
return '';
}
}
throw unknownPropertyBinding(property);
}
module.exports.getPropertyValue = getPropertyValue;
/**
* Return an update operation that changes the diagram
* element's custom property to the given value.
*
* The response of this method will be processed via
* {@link PropertiesPanel#applyChanges}.
*
* @param {djs.model.Base} element
* @param {PropertyDescriptor} property
* @param {String} value
* @param {BpmnFactory} bpmnFactory
*
* @return {Object|Array<Object>} results to be processed
*/
function setPropertyValue(element, property, value, bpmnFactory) {
var bo = getBusinessObject(element);
var binding = property.binding,
scope = property.scope;
var bindingType = binding.type,
bindingName = binding.name;
var propertyValue;
var updates = [];
var extensionElements;
if (EXTENSION_BINDING_TYPES.indexOf(bindingType) !== -1) {
extensionElements = bo.get('extensionElements');
// create extension elements, if they do not exist (yet)
if (!extensionElements) {
extensionElements = elementHelper.createElement('bpmn:ExtensionElements', null, element, bpmnFactory);
updates.push(cmdHelper.updateBusinessObject(
element, bo, objectWithKey('extensionElements', extensionElements)
));
}
}
if (scope) {
bo = findExtension(bo, scope.name);
if (!bo) {
bo = elementHelper.createElement(scope.name, null, element, bpmnFactory);
updates.push(cmdHelper.addElementsTolist(
bo, extensionElements, 'values', [ bo ]
));
}
}
// property
if (bindingType === 'property') {
if (bindingName === 'conditionExpression') {
propertyValue = elementHelper.createElement('bpmn:FormalExpression', {
body: value,
language: binding.scriptFormat
}, bo, bpmnFactory);
} else {
var moddlePropertyDescriptor = bo.$descriptor.propertiesByName[bindingName];
var moddleType = moddlePropertyDescriptor.type;
// make sure we only update String, Integer, Real and
// Boolean properties (do not accidentally override complex objects...)
if (BASIC_MODDLE_TYPES.indexOf(moddleType) === -1) {
throw new Error('cannot set moddle type <' + moddleType + '>');
}
if (moddleType === 'Boolean') {
propertyValue = !!value;
} else
if (moddleType === 'Integer') {
propertyValue = parseInt(value, 10);
if (isNaN(propertyValue)) {
// do not write NaN value
propertyValue = undefined;
}
} else {
propertyValue = value;
}
}
if (propertyValue !== undefined) {
updates.push(cmdHelper.updateBusinessObject(
element, bo, objectWithKey(bindingName, propertyValue)
));
}
}
// activiti:property
var activitiProperties,
existingActivitiProperty,
newActivitiProperty;
if (bindingType === ACTIVITI_PROPERTY_TYPE) {
if (scope) {
activitiProperties = bo.get('properties');
} else {
activitiProperties = findExtension(extensionElements, 'activiti:Properties');
}
if (!activitiProperties) {
activitiProperties = elementHelper.createElement('activiti:Properties', null, bo, bpmnFactory);
if (scope) {
updates.push(cmdHelper.updateBusinessObject(
element, bo, { properties: activitiProperties }
));
}
else {
updates.push(cmdHelper.addElementsTolist(
element, extensionElements, 'values', [ activitiProperties ]
));
}
}
existingActivitiProperty = findActivitiProperty(activitiProperties, binding);
newActivitiProperty = createActivitiProperty(binding, value, bpmnFactory);
updates.push(cmdHelper.addAndRemoveElementsFromList(
element,
activitiProperties,
'values',
null,
[ newActivitiProperty ],
existingActivitiProperty ? [ existingActivitiProperty ] : []
));
}
// activiti:inputParameter
// activiti:outputParameter
var inputOutput,
existingIoParameter,
newIoParameter;
if (IO_BINDING_TYPES.indexOf(bindingType) !== -1) {
if (scope) {
inputOutput = bo.get('inputOutput');
} else {
inputOutput = findExtension(extensionElements, 'activiti:InputOutput');
}
// create inputOutput element, if it do not exist (yet)
if (!inputOutput) {
inputOutput = elementHelper.createElement('activiti:InputOutput', null, bo, bpmnFactory);
if (scope) {
updates.push(cmdHelper.updateBusinessObject(
element, bo, { inputOutput: inputOutput }
));
}
else {
updates.push(cmdHelper.addElementsTolist(
element, extensionElements, 'values', inputOutput
));
}
}
}
if (bindingType === ACTIVITI_INPUT_PARAMETER_TYPE) {
existingIoParameter = findInputParameter(inputOutput, binding);
newIoParameter = createInputParameter(binding, value, bpmnFactory);
updates.push(cmdHelper.addAndRemoveElementsFromList(
element,
inputOutput,
'inputParameters',
null,
[ newIoParameter ],
existingIoParameter ? [ existingIoParameter ] : []
));
}
if (bindingType === ACTIVITI_OUTPUT_PARAMETER_TYPE) {
existingIoParameter = findOutputParameter(inputOutput, binding);
newIoParameter = createOutputParameter(binding, value, bpmnFactory);
updates.push(cmdHelper.addAndRemoveElementsFromList(
element,
inputOutput,
'outputParameters',
null,
[ newIoParameter ],
existingIoParameter ? [ existingIoParameter ] : []
));
}
// activiti:in
// activiti:out
// activiti:in:businessKey
var existingInOut,
newInOut;
if (IN_OUT_BINDING_TYPES.indexOf(bindingType) !== -1) {
existingInOut = findActivitiInOut(bo, binding);
if (bindingType === ACTIVITI_IN_TYPE) {
newInOut = createActivitiIn(binding, value, bpmnFactory);
} else
if (bindingType === ACTIVITI_OUT_TYPE) {
newInOut = createActivitiOut(binding, value, bpmnFactory);
} else {
newInOut = createActivitiInWithBusinessKey(binding, value, bpmnFactory);
}
updates.push(cmdHelper.addAndRemoveElementsFromList(
element,
extensionElements,
'values',
null,
[ newInOut ],
existingInOut ? [ existingInOut ] : []
));
}
if (bindingType === ACTIVITI_FIELD) {
var existingFieldInjections = findExtensions(bo, [ 'activiti:Field' ]);
var newFieldInjections = [];
if (existingFieldInjections.length > 0) {
existingFieldInjections.forEach(function(item) {
if (item.name === binding.name) {
newFieldInjections.push(createActivitiFieldInjection(binding, value, bpmnFactory));
} else {
newFieldInjections.push(item);
}
});
} else {
newFieldInjections.push(createActivitiFieldInjection(binding, value, bpmnFactory));
}
updates.push(cmdHelper.addAndRemoveElementsFromList(
element,
extensionElements,
'values',
null,
newFieldInjections,
existingFieldInjections ? existingFieldInjections : []
));
}
if (updates.length) {
return updates;
}
// quick warning for better debugging
console.warn('no update', element, property, value);
}
module.exports.setPropertyValue = setPropertyValue;
/**
* Validate value of a given property.
*
* @param {String} value
* @param {PropertyDescriptor} property
* @param {Function} translate
*
* @return {Object} with validation errors
*/
function validateValue(value, property, translate) {
var constraints = property.constraints || {};
if (constraints.notEmpty && isEmpty(value)) {
return translate('Must not be empty');
}
if (constraints.maxLength && value.length > constraints.maxLength) {
return translate('Must have max length {length}', { length: constraints.maxLength });
}
if (constraints.minLength && value.length < constraints.minLength) {
return translate('Must have min length {length}', { length: constraints.minLength });
}
var pattern = constraints.pattern,
message;
if (pattern) {
if (typeof pattern !== 'string') {
message = pattern.message;
pattern = pattern.value;
}
if (!matchesPattern(value, pattern)) {
return message || translate('Must match pattern {pattern}', { pattern: pattern });
}
}
}
// misc helpers ///////////////////////////////
function propertyWithScope(property, scopeName) {
if (!scopeName) {
return property;
}
return assign({}, property, {
scope: {
name: scopeName
}
});
}
/**
* Return an object with a single key -> value association.
*
* @param {String} key
* @param {Any} value
*
* @return {Object}
*/
function objectWithKey(key, value) {
var obj = {};
obj[key] = value;
return obj;
}
/**
* Does the given string match the specified pattern?
*
* @param {String} str
* @param {String} pattern
*
* @return {Boolean}
*/
function matchesPattern(str, pattern) {
var regexp = new RegExp(pattern);
return regexp.test(str);
}
function isEmpty(str) {
return !str || /^\s*$/.test(str);
}
/**
* Create a new {@link Error} indicating an unknown
* property binding.
*
* @param {PropertyDescriptor} property
*
* @return {Error}
*/
function unknownPropertyBinding(property) {
var binding = property.binding;
return new Error('unknown binding: <' + binding.type + '>');
}

@ -0,0 +1,16 @@
'use strict';
var Validator = require('../Validator');
/**
* Validate the given template descriptors and
* return a list of errors.
*
* @param {Array<TemplateDescriptor>} descriptors
*
* @return {Array<Error>}
*/
module.exports = function validate(descriptors) {
return new Validator().addAll(descriptors).getErrors();
};

@ -0,0 +1,9 @@
module.exports = {
__depends__: [
require('./element-templates/index'),
require('diagram-js/lib/i18n/translate').default
],
__init__: [ 'propertiesProvider' ],
propertiesProvider: [ 'type', require('./ActivitiPropertiesProvider') ]
};

@ -0,0 +1,16 @@
'use strict';
var getBusinessObject = require('bpmn-js/lib/util/ModelUtil').getBusinessObject,
is = require('bpmn-js/lib/util/ModelUtil').is,
asyncContinuation = require('./implementation/AsyncContinuation');
module.exports = function(group, element, bpmnFactory, translate) {
if (is(element, 'activiti:AsyncCapable')) {
group.entries = group.entries.concat(asyncContinuation(element, bpmnFactory, {
getBusinessObject: getBusinessObject
}, translate));
}
};

@ -0,0 +1,90 @@
'use strict';
var getBusinessObject = require('bpmn-js/lib/util/ModelUtil').getBusinessObject,
is = require('bpmn-js/lib/util/ModelUtil').is;
var entryFactory = require('../../../factory/EntryFactory');
var callable = require('./implementation/Callable');
var cmdHelper = require('../../../helper/CmdHelper');
var flattenDeep = require('lodash/flattenDeep');
var assign = require('lodash/assign');
function getCallableType(element) {
var bo = getBusinessObject(element);
var boCalledElement = bo.get('calledElement'),
boCaseRef = bo.get('activiti:caseRef');
var callActivityType = '';
if (typeof boCalledElement !== 'undefined') {
callActivityType = 'bpmn';
} else
if (typeof boCaseRef !== 'undefined') {
callActivityType = 'cmmn';
}
return callActivityType;
}
var DEFAULT_PROPS = {
calledElement: undefined,
'activiti:calledElementBinding': 'latest',
'activiti:calledElementVersion': undefined,
'activiti:calledElementTenantId': undefined,
'activiti:variableMappingClass' : undefined,
'activiti:variableMappingDelegateExpression' : undefined,
'activiti:caseRef': undefined,
'activiti:caseBinding': 'latest',
'activiti:caseVersion': undefined,
'activiti:caseTenantId': undefined
};
module.exports = function(group, element, bpmnFactory, translate) {
if (!is(element, 'activiti:CallActivity')) {
return;
}
group.entries.push(entryFactory.selectBox({
id : 'callActivity',
label: translate('CallActivity Type'),
selectOptions: [
{ name: 'BPMN', value: 'bpmn' },
{ name: 'CMMN', value: 'cmmn' }
],
emptyParameter: true,
modelProperty: 'callActivityType',
get: function(element, node) {
return {
callActivityType: getCallableType(element)
};
},
set: function(element, values, node) {
var type = values.callActivityType;
var props = assign({}, DEFAULT_PROPS);
if (type === 'bpmn') {
props.calledElement = '';
}
else if (type === 'cmmn') {
props['activiti:caseRef'] = '';
}
return cmdHelper.updateProperties(element, props);
}
}));
group.entries.push(callable(element, bpmnFactory, {
getCallableType: getCallableType
}, translate));
group.entries = flattenDeep(group.entries);
};

@ -0,0 +1,27 @@
'use strict';
var is = require('bpmn-js/lib/util/ModelUtil').is,
getBusinessObject = require('bpmn-js/lib/util/ModelUtil').getBusinessObject;
var candidateStarter = require('./implementation/CandidateStarter');
module.exports = function(group, element, bpmnFactory, translate) {
var businessObject = getBusinessObject(element);
if (is(element, 'activiti:Process') ||
is(element, 'bpmn:Participant') && businessObject.get('processRef')) {
group.entries = group.entries.concat(candidateStarter(element, bpmnFactory, {
getBusinessObject: function(element) {
var bo = getBusinessObject(element);
if (!is(bo, 'bpmn:Participant')) {
return bo;
}
return bo.get('processRef');
}
}, translate));
}
};

@ -0,0 +1,185 @@
'use strict';
var is = require('bpmn-js/lib/util/ModelUtil').is,
isAny = require('bpmn-js/lib/features/modeling/util/ModelingUtil').isAny,
getBusinessObject = require('bpmn-js/lib/util/ModelUtil').getBusinessObject,
escapeHTML = require('../../../Utils').escapeHTML,
domQuery = require('min-dom').query,
cmdHelper = require('../../../helper/CmdHelper'),
elementHelper = require('../../../helper/ElementHelper'),
eventDefinitionHelper = require('../../../helper/EventDefinitionHelper'),
scriptImplementation = require('./implementation/Script');
module.exports = function(group, element, bpmnFactory, translate) {
var bo = getBusinessObject(element);
if (!bo) {
return;
}
var conditionalEventDefinition = eventDefinitionHelper.getConditionalEventDefinition(element);
if (!(is(element, 'bpmn:SequenceFlow') && isConditionalSource(element.source))
&& !conditionalEventDefinition) {
return;
}
var script = scriptImplementation('language', 'body', true, translate);
group.entries.push({
id: 'condition',
label: translate('Condition'),
html: '<div class="bpp-row">' +
'<label for="cam-condition-type">'+ escapeHTML(translate('Condition Type')) + '</label>' +
'<div class="bpp-field-wrapper">' +
'<select id="cam-condition-type" name="conditionType" data-value>' +
'<option value="expression">'+ escapeHTML(translate('Expression')) + '</option>' +
'<option value="script">'+ escapeHTML(translate('Script')) + '</option>' +
'<option value="" selected></option>' +
'</select>' +
'</div>' +
'</div>' +
// expression
'<div class="bpp-row">' +
'<label for="cam-condition" data-show="isExpression">' + escapeHTML(translate('Expression')) + '</label>' +
'<div class="bpp-field-wrapper" data-show="isExpression">' +
'<input id="cam-condition" type="text" name="condition" />' +
'<button class="clear" data-action="clear" data-show="canClear">' +
'<span>X</span>' +
'</button>' +
'</div>' +
'<div data-show="isScript">' +
script.template +
'</div>' +
'</div>',
get: function(element, propertyName) {
var conditionalEventDefinition = eventDefinitionHelper.getConditionalEventDefinition(element);
var conditionExpression = conditionalEventDefinition
? conditionalEventDefinition.condition
: bo.conditionExpression;
var values = {},
conditionType = '';
if (conditionExpression) {
var conditionLanguage = conditionExpression.language;
if (typeof conditionLanguage !== 'undefined') {
conditionType = 'script';
values = script.get(element, conditionExpression);
} else {
conditionType = 'expression';
values.condition = conditionExpression.get('body');
}
}
values.conditionType = conditionType;
return values;
},
set: function(element, values, containerElement) {
var conditionType = values.conditionType;
var commands = [];
var conditionProps = {
body: undefined
};
if (conditionType === 'script') {
conditionProps = script.set(element, values, containerElement);
} else {
var condition = values.condition;
conditionProps.body = condition;
}
var conditionOrConditionExpression;
if (conditionType) {
conditionOrConditionExpression = elementHelper.createElement(
'bpmn:FormalExpression',
conditionProps,
conditionalEventDefinition || bo,
bpmnFactory
);
var source = element.source;
// if default-flow, remove default-property from source
if (source && source.businessObject.default === bo) {
commands.push(cmdHelper.updateProperties(source, { 'default': undefined }));
}
}
var update = conditionalEventDefinition
? { condition: conditionOrConditionExpression }
: { conditionExpression: conditionOrConditionExpression };
commands.push(cmdHelper.updateBusinessObject(element, conditionalEventDefinition || bo, update));
return commands;
},
validate: function(element, values) {
var validationResult = {};
if (!values.condition && values.conditionType === 'expression') {
validationResult.condition = translate('Must provide a value');
}
else if (values.conditionType === 'script') {
validationResult = script.validate(element, values);
}
return validationResult;
},
isExpression: function(element, inputNode) {
var conditionType = domQuery('select[name=conditionType]', inputNode);
if (conditionType.selectedIndex >= 0) {
return conditionType.options[conditionType.selectedIndex].value === 'expression';
}
},
isScript: function(element, inputNode) {
var conditionType = domQuery('select[name=conditionType]', inputNode);
if (conditionType.selectedIndex >= 0) {
return conditionType.options[conditionType.selectedIndex].value === 'script';
}
},
clear: function(element, inputNode) {
// clear text input
domQuery('input[name=condition]', inputNode).value='';
return true;
},
canClear: function(element, inputNode) {
var input = domQuery('input[name=condition]', inputNode);
return input.value !== '';
},
script : script,
cssClasses: [ 'bpp-textfield' ]
});
};
// utilities //////////////////////////
var CONDITIONAL_SOURCES = [
'bpmn:Activity',
'bpmn:ExclusiveGateway',
'bpmn:InclusiveGateway',
'bpmn:ComplexGateway'
];
function isConditionalSource(element) {
return isAny(element, CONDITIONAL_SOURCES);
}

@ -0,0 +1,57 @@
'use strict';
var ImplementationTypeHelper = require('../../../helper/ImplementationTypeHelper'),
InputOutputHelper = require('../../../helper/InputOutputHelper');
var entryFactory = require('../../../factory/EntryFactory'),
cmdHelper = require('../../../helper/CmdHelper');
function getImplementationType(element) {
return ImplementationTypeHelper.getImplementationType(element);
}
function getBusinessObject(element) {
return ImplementationTypeHelper.getServiceTaskLikeBusinessObject(element);
}
function getConnector(bo) {
return InputOutputHelper.getConnector(bo);
}
function isConnector(element) {
return getImplementationType(element) === 'connector';
}
module.exports = function(group, element, bpmnFactory, translate) {
group.entries.push(entryFactory.textField({
id: 'connectorId',
label: translate('Connector Id'),
modelProperty: 'connectorId',
get: function(element, node) {
var bo = getBusinessObject(element);
var connector = bo && getConnector(bo);
var value = connector && connector.get('connectorId');
return { connectorId: value };
},
set: function(element, values, node) {
var bo = getBusinessObject(element);
var connector = getConnector(bo);
return cmdHelper.updateBusinessObject(element, connector, {
connectorId: values.connectorId || undefined
});
},
validate: function(element, values, node) {
return isConnector(element) && !values.connectorId ? { connectorId: translate('Must provide a value') } : {};
},
hidden: function(element, node) {
return !isConnector(element);
}
}));
};

@ -0,0 +1,16 @@
'use strict';
var assign = require('lodash/assign');
var inputOutputParameter = require('./implementation/InputOutputParameter');
module.exports = function(group, element, bpmnFactory, options, translate) {
options = assign({
idPrefix: 'connector-',
insideConnector: true
}, options);
group.entries = group.entries.concat(inputOutputParameter(element, bpmnFactory, options, translate));
};

@ -0,0 +1,18 @@
'use strict';
var inputOutput = require('./implementation/InputOutput');
module.exports = function(group, element, bpmnFactory, translate) {
var inputOutputEntry = inputOutput(element, bpmnFactory, {
idPrefix: 'connector-',
insideConnector: true
}, translate);
group.entries = group.entries.concat(inputOutputEntry.entries);
return {
getSelectedParameter: inputOutputEntry.getSelectedParameter
};
};

@ -0,0 +1,40 @@
'use strict';
var is = require('bpmn-js/lib/util/ModelUtil').is,
eventDefinitionHelper = require('../../../helper/EventDefinitionHelper'),
error = require('./implementation/ErrorEventDefinition');
var forEach = require('lodash/forEach');
module.exports = function(group, element, bpmnFactory, translate) {
var errorEvents = [
'bpmn:StartEvent',
'bpmn:BoundaryEvent',
'bpmn:EndEvent'
];
forEach(errorEvents, function(event) {
if (is(element, event)) {
var errorEventDefinition = eventDefinitionHelper.getErrorEventDefinition(element);
if (errorEventDefinition) {
var isCatchingErrorEvent = is(element, 'bpmn:StartEvent') || is (element, 'bpmn:BoundaryEvent');
var showErrorCodeVariable = isCatchingErrorEvent,
showErrorMessageVariable = isCatchingErrorEvent;
error(
group,
element,
bpmnFactory,
errorEventDefinition,
showErrorCodeVariable,
showErrorMessageVariable,
translate);
}
}
});
};

@ -0,0 +1,40 @@
'use strict';
var is = require('bpmn-js/lib/util/ModelUtil').is,
getBusinessObject = require('bpmn-js/lib/util/ModelUtil').getBusinessObject;
var ImplementationTypeHelper = require('../../../helper/ImplementationTypeHelper');
var externalTaskPriority = require('./implementation/ExternalTaskPriority');
function getServiceTaskLikeBusinessObject(element) {
var bo = ImplementationTypeHelper.getServiceTaskLikeBusinessObject(element);
// if the element is not a serviceTaskLike element, fetch the normal business object
// This avoids the loss of the process / participant business object
if (!bo) {
bo = getBusinessObject(element);
}
return bo;
}
module.exports = function(group, element, bpmnFactory, translate) {
var bo = getServiceTaskLikeBusinessObject(element);
if (!bo) {
return;
}
if (is(bo, 'activiti:TaskPriorized') || (is(bo, 'bpmn:Participant')) && bo.get('processRef')) {
group.entries = group.entries.concat(externalTaskPriority(element, bpmnFactory, {
getBusinessObject: function(element) {
if (!is(bo, 'bpmn:Participant')) {
return bo;
}
return bo.get('processRef');
}
}, translate));
}
};

@ -0,0 +1,21 @@
'use strict';
var ImplementationTypeHelper = require('../../../helper/ImplementationTypeHelper');
var fieldInjection = require('./implementation/FieldInjection');
module.exports = function(group, element, bpmnFactory, translate) {
var bo = ImplementationTypeHelper.getServiceTaskLikeBusinessObject(element);
if (!bo) {
return;
}
var fieldInjectionEntry = fieldInjection(element, bpmnFactory, translate, { businessObject: bo });
if (fieldInjectionEntry && fieldInjectionEntry.length > 0) {
group.entries = group.entries.concat(fieldInjectionEntry);
}
};

@ -0,0 +1,476 @@
'use strict';
var getBusinessObject = require('bpmn-js/lib/util/ModelUtil').getBusinessObject,
getExtensionElements = require('../../../helper/ExtensionElementsHelper').getExtensionElements,
removeEntry = require('../../../helper/ExtensionElementsHelper').removeEntry,
extensionElements = require('./implementation/ExtensionElements'),
properties = require('./implementation/Properties'),
entryFactory = require('../../../factory/EntryFactory'),
elementHelper = require('../../../helper/ElementHelper'),
cmdHelper = require('../../../helper/CmdHelper'),
formHelper = require('../../../helper/FormHelper'),
utils = require('../../../Utils'),
is = require('bpmn-js/lib/util/ModelUtil').is,
find = require('lodash/find'),
each = require('lodash/forEach');
function generateValueId() {
return utils.nextId('Value_');
}
/**
* Generate a form field specific textField using entryFactory.
*
* @param {string} options.id
* @param {string} options.label
* @param {string} options.modelProperty
* @param {function} options.validate
*
* @return {Object} an entryFactory.textField object
*/
function formFieldTextField(options, getSelectedFormField) {
var id = options.id,
label = options.label,
modelProperty = options.modelProperty,
validate = options.validate;
return entryFactory.textField({
id: id,
label: label,
modelProperty: modelProperty,
get: function(element, node) {
var selectedFormField = getSelectedFormField(element, node) || {},
values = {};
values[modelProperty] = selectedFormField[modelProperty];
return values;
},
set: function(element, values, node) {
var commands = [];
if (typeof options.set === 'function') {
var cmd = options.set(element, values, node);
if (cmd) {
commands.push(cmd);
}
}
var formField = getSelectedFormField(element, node),
properties = {};
properties[modelProperty] = values[modelProperty] || undefined;
commands.push(cmdHelper.updateBusinessObject(element, formField, properties));
return commands;
},
hidden: function(element, node) {
return !getSelectedFormField(element, node);
},
validate: validate
});
}
function ensureFormKeyAndDataSupported(element) {
return (
is(element, 'bpmn:StartEvent') && !is(element.parent, 'bpmn:SubProcess')
) || is(element, 'bpmn:UserTask');
}
module.exports = function(group, element, bpmnFactory, translate) {
if (!ensureFormKeyAndDataSupported(element)) {
return;
}
/**
* Return the currently selected form field querying the form field select box
* from the DOM.
*
* @param {djs.model.Base} element
* @param {DOMElement} node - DOM element of any form field text input
*
* @return {ModdleElement} the currently selected form field
*/
function getSelectedFormField(element, node) {
var selected = formFieldsEntry.getSelected(element, node.parentNode);
if (selected.idx === -1) {
return;
}
return formHelper.getFormField(element, selected.idx);
}
// [FormKey] form key text input field
group.entries.push(entryFactory.textField({
id : 'form-key',
label : translate('Form Key'),
modelProperty: 'formKey',
get: function(element, node) {
var bo = getBusinessObject(element);
return {
formKey: bo.get('activiti:formKey')
};
},
set: function(element, values, node) {
var bo = getBusinessObject(element),
formKey = values.formKey || undefined;
return cmdHelper.updateBusinessObject(element, bo, { 'activiti:formKey': formKey });
}
}));
// [FormData] form field select box
var formFieldsEntry = extensionElements(element, bpmnFactory, {
id: 'form-fields',
label: translate('Form Fields'),
modelProperty: 'id',
prefix: 'FormProperty',
createExtensionElement: function(element, extensionElements, value) {
var bo = getBusinessObject(element), commands = [];
if (!extensionElements) {
extensionElements = elementHelper.createElement('bpmn:ExtensionElements', { values: [] }, bo, bpmnFactory);
commands.push(cmdHelper.updateProperties(element, { extensionElements: extensionElements }));
}
/* var formData = formHelper.getFormData(element);
if (!formData) {
formData = elementHelper.createElement('activiti:FormData', { fields: [] }, extensionElements, bpmnFactory);
commands.push(cmdHelper.addAndRemoveElementsFromList(
element,
extensionElements,
'values',
'extensionElements',
[formData],
[]
));
}*/
/**activiti 是向 extensionElements 下追加元素,而不是 formData 下,*/
var field = elementHelper.createElement('activiti:FormProperty', { id: value }, extensionElements, bpmnFactory);
if (typeof extensionElements.values !== 'undefined') {
commands.push(cmdHelper.addElementsTolist(element, extensionElements, 'values', [ field ]));
} else {
commands.push(cmdHelper.updateBusinessObject(element, extensionElements, {
values: [ field ]
}));
}
return commands;
},
removeExtensionElement: function(element, extensionElements, value, idx) {
var fields = formHelper.getFormFields(element);
var entry = fields[idx],
commands = [];
if (fields.length < 2) {
commands.push(removeEntry(getBusinessObject(element), element, extensionElements));
} else {
commands.push(cmdHelper.removeElementsFromList(element, extensionElements, 'values', null, [entry]));
/* if (entry.id === formData.get('businessKey')) {
commands.push(cmdHelper.updateBusinessObject(element, extensionElements, { 'businessKey': undefined }));
}*/
}
return commands;
},
getExtensionElements: function(element) {
return formHelper.getFormFields(element);
},
hideExtensionElements: function(element, node) {
return false;
}
});
group.entries.push(formFieldsEntry);
// [FormData] Form Field label
group.entries.push(entryFactory.label({
id: 'form-field-header',
labelText: translate('Form Field'),
showLabel: function(element, node) {
return !!getSelectedFormField(element, node);
}
}));
// [FormData] form field id text input field
group.entries.push(entryFactory.validationAwareTextField({
id: 'form-field-id',
label: translate('ID'),
modelProperty: 'id',
getProperty: function(element, node) {
var selectedFormField = getSelectedFormField(element, node) || {};
return selectedFormField.id;
},
setProperty: function(element, properties, node) {
var formField = getSelectedFormField(element, node);
return cmdHelper.updateBusinessObject(element, formField, properties);
},
hidden: function(element, node) {
return !getSelectedFormField(element, node);
},
validate: function(element, values, node) {
var formField = getSelectedFormField(element, node);
if (formField) {
var idValue = values.id;
if (!idValue || idValue.trim() === '') {
return { id: 'Form field id must not be empty' };
}
var formFields = formHelper.getFormFields(element);
var existingFormField = find(formFields, function(f) {
return f !== formField && f.id === idValue;
});
if (existingFormField) {
return { id: 'Form field id already used in form data.' };
}
}
}
}));
// [FormData] form field type combo box
group.entries.push(entryFactory.comboBox({
id: 'form-field-type',
label: translate('Type'),
selectOptions: [
{ name: 'string', value: 'string' },
{ name: 'long', value: 'long' },
{ name: 'boolean', value: 'boolean' },
{ name: 'date', value: 'date' },
{ name: 'enum', value: 'enum' }
],
modelProperty: 'type',
emptyParameter: true,
get: function(element, node) {
var selectedFormField = getSelectedFormField(element, node);
if (selectedFormField) {
return { type: selectedFormField.type };
} else {
return {};
}
},
set: function(element, values, node) {
var selectedFormField = getSelectedFormField(element, node),
commands = [];
if (selectedFormField.type === 'enum' && values.type !== 'enum') {
// delete activiti:value objects from formField.values when switching from type enum
commands.push(cmdHelper.updateBusinessObject(element, selectedFormField, { values: undefined }));
}
commands.push(cmdHelper.updateBusinessObject(element, selectedFormField, values));
return commands;
},
hidden: function(element, node) {
return !getSelectedFormField(element, node);
}
}));
// [FormData] form field label text input field
group.entries.push(formFieldTextField({
id: 'form-field-label',
label: translate('Label'),
modelProperty: 'label'
}, getSelectedFormField));
// [FormData] form field defaultValue text input field
group.entries.push(formFieldTextField({
id: 'form-field-defaultValue',
label: translate('Default Value'),
modelProperty: 'defaultValue'
}, getSelectedFormField));
// [FormData] form field enum values label
group.entries.push(entryFactory.label({
id: 'form-field-enum-values-header',
labelText: translate('Values'),
divider: true,
showLabel: function(element, node) {
var selectedFormField = getSelectedFormField(element, node);
return selectedFormField && selectedFormField.type === 'enum';
}
}));
// [FormData] form field enum values table
group.entries.push(entryFactory.table({
id: 'form-field-enum-values',
labels: [ translate('Id'), translate('Name') ],
modelProperties: [ 'id', 'name' ],
addLabel:translate('Add Value'),
show: function(element, node) {
var selectedFormField = getSelectedFormField(element, node);
return selectedFormField && selectedFormField.type === 'enum';
},
getElements: function(element, node) {
var selectedFormField = getSelectedFormField(element, node);
return formHelper.getEnumValues(selectedFormField);
},
addElement: function(element, node) {
var selectedFormField = getSelectedFormField(element, node),
id = generateValueId();
var enumValue = elementHelper.createElement(
'activiti:Value',
{ id: id, name: undefined },
getBusinessObject(element),
bpmnFactory
);
return cmdHelper.addElementsTolist(element, selectedFormField, 'values', [enumValue]);
},
removeElement: function(element, node, idx) {
var selectedFormField = getSelectedFormField(element, node),
enumValue = selectedFormField.values[idx];
return cmdHelper.removeElementsFromList(element, selectedFormField, 'values', null, [enumValue]);
},
updateElement: function(element, value, node, idx) {
var selectedFormField = getSelectedFormField(element, node),
enumValue = selectedFormField.values[idx];
value.name = value.name || undefined;
return cmdHelper.updateBusinessObject(element, enumValue, value);
},
validate: function(element, value, node, idx) {
var selectedFormField = getSelectedFormField(element, node),
enumValue = selectedFormField.values[idx];
if (enumValue) {
// check if id is valid
var validationError = utils.isIdValid(enumValue, value.id, translate);
if (validationError) {
return { id: validationError };
}
}
}
}));
// [FormData] Validation label
group.entries.push(entryFactory.label({
id: 'form-field-validation-header',
labelText: translate('Validation'),
divider: true,
showLabel: function(element, node) {
return !!getSelectedFormField(element, node);
}
}));
// [FormData] form field constraints table
group.entries.push(entryFactory.table({
id: 'constraints-list',
modelProperties: [ 'name', 'config' ],
labels: [ translate('Name'), translate('Config') ],
addLabel: translate('Add Constraint'),
getElements: function(element, node) {
var formField = getSelectedFormField(element, node);
return formHelper.getConstraints(formField);
},
addElement: function(element, node) {
var commands = [],
formField = getSelectedFormField(element, node),
validation = formField.validation;
if (!validation) {
// create validation business object and add it to form data, if it doesn't exist
validation = elementHelper.createElement('activiti:Validation', {}, getBusinessObject(element), bpmnFactory);
commands.push(cmdHelper.updateBusinessObject(element, formField, { 'validation': validation }));
}
var newConstraint = elementHelper.createElement(
'activiti:Constraint',
{ name: undefined, config: undefined },
validation,
bpmnFactory
);
commands.push(cmdHelper.addElementsTolist(element, validation, 'constraints', [ newConstraint ]));
return commands;
},
updateElement: function(element, value, node, idx) {
var formField = getSelectedFormField(element, node),
constraint = formHelper.getConstraints(formField)[idx];
value.name = value.name || undefined;
value.config = value.config || undefined;
return cmdHelper.updateBusinessObject(element, constraint, value);
},
removeElement: function(element, node, idx) {
var commands = [],
formField = getSelectedFormField(element, node),
constraints = formHelper.getConstraints(formField),
currentConstraint = constraints[idx];
commands.push(cmdHelper.removeElementsFromList(
element,
formField.validation,
'constraints',
null,
[ currentConstraint ]
));
if (constraints.length === 1) {
// remove activiti:validation if the last existing constraint has been removed
commands.push(cmdHelper.updateBusinessObject(element, formField, { validation: undefined }));
}
return commands;
},
show: function(element, node) {
return !!getSelectedFormField(element, node);
}
}));
// [FormData] Properties label
group.entries.push(entryFactory.label({
id: 'form-field-properties-header',
labelText: translate('Properties'),
divider: true,
showLabel: function(element, node) {
return !!getSelectedFormField(element, node);
}
}));
// [FormData] activiti:properties table
group.entries.push(properties(element, bpmnFactory, {
id: 'form-field-properties',
modelProperties: [ 'id', 'value' ],
labels: [ translate('Id'), translate('Value') ],
getParent: function(element, node) {
return getSelectedFormField(element, node);
},
show: function(element, node) {
return !!getSelectedFormField(element, node);
}
}, translate));
};

@ -0,0 +1,27 @@
'use strict';
var is = require('bpmn-js/lib/util/ModelUtil').is,
getBusinessObject = require('bpmn-js/lib/util/ModelUtil').getBusinessObject;
var historyTimeToLive = require('./implementation/HistoryTimeToLive');
module.exports = function(group, element, bpmnFactory, translate) {
var businessObject = getBusinessObject(element);
if (is(element, 'activiti:Process') ||
is(element, 'bpmn:Participant') && businessObject.get('processRef')) {
group.entries = group.entries.concat(historyTimeToLive(element, bpmnFactory, {
getBusinessObject: function(element) {
var bo = getBusinessObject(element);
if (!is(bo, 'bpmn:Participant')) {
return bo;
}
return bo.get('processRef');
}
}, translate));
}
};

@ -0,0 +1,11 @@
'use strict';
var inputOutputParameter = require('./implementation/InputOutputParameter');
var assign = require('lodash/assign');
module.exports = function(group, element, bpmnFactory, options, translate) {
group.entries = group.entries.concat(inputOutputParameter(element, bpmnFactory, assign({}, options), translate));
};

@ -0,0 +1,15 @@
'use strict';
var inputOutput = require('./implementation/InputOutput');
module.exports = function(group, element, bpmnFactory, translate) {
var inputOutputEntry = inputOutput(element, bpmnFactory, {}, translate);
group.entries = group.entries.concat(inputOutputEntry.entries);
return {
getSelectedParameter: inputOutputEntry.getSelectedParameter
};
};

@ -0,0 +1,34 @@
'use strict';
var is = require('bpmn-js/lib/util/ModelUtil').is,
getBusinessObject = require('bpmn-js/lib/util/ModelUtil').getBusinessObject;
var jobPriority = require('./implementation/JobPriority'),
jobRetryTimeCycle = require('./implementation/JobRetryTimeCycle');
module.exports = function(group, element, bpmnFactory, translate) {
var businessObject = getBusinessObject(element);
if (is(element, 'activiti:JobPriorized') ||
is(element, 'bpmn:Participant') && businessObject.get('processRef')) {
group.entries = group.entries.concat(jobPriority(element, bpmnFactory, {
getBusinessObject: function(element) {
var bo = getBusinessObject(element);
if (!is(bo, 'bpmn:Participant')) {
return bo;
}
return bo.get('processRef');
}
}, translate));
}
if (is(element, 'activiti:AsyncCapable')) {
group.entries = group.entries.concat(jobRetryTimeCycle(element, bpmnFactory, {
getBusinessObject: getBusinessObject
}, translate));
}
};

@ -0,0 +1,208 @@
'use strict';
var entryFactory = require('../../../factory/EntryFactory');
var cmdHelper = require('../../../helper/CmdHelper'),
ImplementationTypeHelper = require('../../../helper/ImplementationTypeHelper'),
scriptImplementation = require('./implementation/Script');
module.exports = function(group, element, bpmnFactory, options, translate) {
var LISTENER_TYPE_LABEL = {
class: translate('Java Class'),
expression: translate('Expression'),
delegateExpression: translate('Delegate Expression'),
script: translate('Script')
};
options = options || {};
var getSelectedListener = options.getSelectedListener;
var classProp = 'class',
expressionProp = 'expression',
delegateExpressionProp = 'delegateExpression',
scriptProp = 'script';
var executionListenerEventTypeOptions = ImplementationTypeHelper.isSequenceFlow(element) ? [
{ name: translate('take'), value: 'take' }
] : [
{ name: translate('start'), value: 'start' },
{ name: translate('end'), value: 'end' }
];
var taskListenerEventTypeOptions = [
{ name: translate('create'), value: 'create' },
{ name: translate('assignment'), value: 'assignment' },
{ name: translate('complete'), value: 'complete' },
{ name: translate('delete'), value: 'delete' }
];
var isSelected = function(element, node) {
return getSelectedListener(element, node);
};
group.entries.push(entryFactory.selectBox({
id: 'listener-event-type',
label: translate('Event Type'),
modelProperty: 'eventType',
emptyParameter: false,
get: function(element, node) {
var listener = getSelectedListener(element, node);
var eventType = listener && listener.get('event');
return {
eventType: eventType
};
},
set: function(element, values, node) {
var eventType = values.eventType;
return cmdHelper.updateBusinessObject(element, getSelectedListener(element, node), { event: eventType });
},
selectOptions: function(element, node) {
var eventTypeOptions;
var selectedListener = getSelectedListener(element, node);
if (ImplementationTypeHelper.isTaskListener(selectedListener)) {
eventTypeOptions = taskListenerEventTypeOptions;
} else if (ImplementationTypeHelper.isExecutionListener(selectedListener)) {
eventTypeOptions = executionListenerEventTypeOptions;
}
return eventTypeOptions;
},
hidden: function(element, node) {
return !isSelected(element, node);
}
}));
group.entries.push(entryFactory.selectBox({
id: 'listener-type',
label: translate('Listener Type'),
selectOptions: [
{ value: classProp, name: translate('Java Class') },
{ value: expressionProp, name: translate('Expression') },
{ value: delegateExpressionProp, name: translate('Delegate Expression') },
{ value: scriptProp, name: translate('Script') }
],
modelProperty: 'listenerType',
emptyParameter: false,
get: function(element, node) {
var listener = getSelectedListener(element, node);
return {
listenerType: ImplementationTypeHelper.getImplementationType(listener)
};
},
set: function(element, values, node) {
var listener = getSelectedListener(element, node),
listenerType = values.listenerType || undefined,
update = {};
update[classProp] = listenerType === classProp ? '' : undefined;
update[expressionProp] = listenerType === expressionProp ? '' : undefined;
update[delegateExpressionProp] = listenerType === delegateExpressionProp ? '' : undefined;
update[scriptProp] = listenerType === scriptProp ? bpmnFactory.create('activiti:Script') : undefined;
return cmdHelper.updateBusinessObject(element, listener, update);
},
hidden: function(element, node) {
return !isSelected(element, node);
}
}));
group.entries.push(entryFactory.textField({
id: 'listener-value',
dataValueLabel: 'listenerValueLabel',
modelProperty: 'listenerValue',
get: function(element, node) {
var value = {},
listener = getSelectedListener(element, node),
listenerType = ImplementationTypeHelper.getImplementationType(listener);
value.listenerValueLabel = LISTENER_TYPE_LABEL[listenerType] || '';
value.listenerValue = (listener && listener.get(listenerType)) || undefined;
return value;
},
set: function(element, values, node) {
var update = {},
listener = getSelectedListener(element, node),
listenerType = ImplementationTypeHelper.getImplementationType(listener);
update[listenerType] = values.listenerValue || '';
return cmdHelper.updateBusinessObject(element, listener, update);
},
hidden: function(element, node) {
var listener = getSelectedListener(element, node);
return !listener || listener.script;
},
validate: function(element, values) {
var value = values.listenerValue,
validate = {};
if (!value) {
validate.listenerValue = translate('Must provide a value');
}
return validate;
}
}));
var script = scriptImplementation('scriptFormat', 'value', true, translate);
group.entries.push({
id: 'listener-script-value',
html: '<div data-show="isScript">' +
script.template +
'</div>',
get: function(element, node) {
var listener = getSelectedListener(element, node);
return listener && listener.script ? script.get(element, listener.script) : {};
},
set: function(element, values, node) {
var listener = getSelectedListener(element, node);
var update = script.set(element, values, listener);
return cmdHelper.updateBusinessObject(element, listener.script, update);
},
validate: function(element, values, node) {
var listener = getSelectedListener(element, node);
return listener && listener.script ? script.validate(element, values) : {};
},
isScript: function(element, node) {
var listener = getSelectedListener(element, node);
return listener && listener.script;
},
script: script
});
};

@ -0,0 +1,20 @@
'use strict';
var assign = require('lodash/assign');
var fieldInjection = require('./implementation/FieldInjection');
module.exports = function(group, element, bpmnFactory, options, translate) {
options = assign({
idPrefix: 'listener-',
insideListener: true
}, options);
var fieldInjectionEntry = fieldInjection(element, bpmnFactory, translate, options);
if (fieldInjectionEntry && fieldInjectionEntry.length > 0) {
group.entries = group.entries.concat(fieldInjectionEntry);
}
};

@ -0,0 +1,15 @@
'use strict';
var listener = require('./implementation/Listener');
module.exports = function(group, element, bpmnFactory, translate) {
var listenerEntry = listener(element, bpmnFactory, {}, translate);
group.entries = group.entries.concat(listenerEntry.entries);
return {
getSelectedListener: listenerEntry.getSelectedListener
};
};

@ -0,0 +1,46 @@
'use strict';
var getBusinessObject = require('bpmn-js/lib/util/ModelUtil').getBusinessObject,
is = require('bpmn-js/lib/util/ModelUtil').is;
var multiInstanceLoopCharacteristics = require('./implementation/MultiInstanceLoopCharacteristics');
var jobRetryTimeCycle = require('./implementation/JobRetryTimeCycle'),
asyncContinuation = require('./implementation/AsyncContinuation');
function getLoopCharacteristics(element) {
var bo = getBusinessObject(element);
return bo.loopCharacteristics;
}
function ensureMultiInstanceSupported(element) {
var loopCharacteristics = getLoopCharacteristics(element);
return !!loopCharacteristics && is(loopCharacteristics, 'activiti:Collectable');
}
module.exports = function(group, element, bpmnFactory, translate) {
if (!ensureMultiInstanceSupported(element)) {
return;
}
// multi instance properties
group.entries = group.entries.concat(multiInstanceLoopCharacteristics(element, bpmnFactory, translate));
// async continuation ///////////////////////////////////////////////////////
group.entries = group.entries.concat(asyncContinuation(element, bpmnFactory, {
getBusinessObject: getLoopCharacteristics,
idPrefix: 'multiInstance-',
labelPrefix: translate('Multi Instance ')
}, translate));
// retry time cycle //////////////////////////////////////////////////////////
group.entries = group.entries.concat(jobRetryTimeCycle(element, bpmnFactory, {
getBusinessObject: getLoopCharacteristics,
idPrefix: 'multiInstance-',
labelPrefix: translate('Multi Instance ')
}, translate));
};

@ -0,0 +1,33 @@
'use strict';
var properties = require('./implementation/Properties'),
elementHelper = require('../../../helper/ElementHelper'),
cmdHelper = require('../../../helper/CmdHelper');
module.exports = function(group, element, bpmnFactory, translate) {
var propertiesEntry = properties(element, bpmnFactory, {
id: 'properties',
modelProperties: [ 'name', 'value' ],
labels: [ translate('Name'), translate('Value') ],
getParent: function(element, node, bo) {
return bo.extensionElements;
},
createParent: function(element, bo) {
var parent = elementHelper.createElement('bpmn:ExtensionElements', { values: [] }, bo, bpmnFactory);
var cmd = cmdHelper.updateBusinessObject(element, bo, { extensionElements: parent });
return {
cmd: cmd,
parent: parent
};
}
}, translate);
if (propertiesEntry) {
group.entries.push(propertiesEntry);
}
};

@ -0,0 +1,68 @@
'use strict';
var getBusinessObject = require('bpmn-js/lib/util/ModelUtil').getBusinessObject,
is = require('bpmn-js/lib/util/ModelUtil').is,
entryFactory = require('../../../factory/EntryFactory'),
cmdHelper = require('../../../helper/CmdHelper'),
scriptImplementation = require('./implementation/Script');
module.exports = function(group, element, bpmnFactory, translate) {
var bo;
if (is(element, 'bpmn:ScriptTask')) {
bo = getBusinessObject(element);
}
if (!bo) {
return;
}
var script = scriptImplementation('scriptFormat', 'script', false, translate);
group.entries.push({
id: 'script-implementation',
label: translate('Script'),
html: script.template,
get: function(element) {
return script.get(element, bo);
},
set: function(element, values, containerElement) {
var properties = script.set(element, values, containerElement);
return cmdHelper.updateProperties(element, properties);
},
validate: function(element, values) {
return script.validate(element, values);
},
script : script,
cssClasses: ['bpp-textfield']
});
group.entries.push(entryFactory.textField({
id : 'scriptResultVariable',
label : translate('Result Variable'),
modelProperty : 'scriptResultVariable',
get: function(element, propertyName) {
var boResultVariable = bo.get('activiti:resultVariable');
return { scriptResultVariable : boResultVariable };
},
set: function(element, values, containerElement) {
return cmdHelper.updateProperties(element, {
'activiti:resultVariable': values.scriptResultVariable.length
? values.scriptResultVariable
: undefined
});
}
}));
};

@ -0,0 +1,151 @@
'use strict';
var ImplementationTypeHelper = require('../../../helper/ImplementationTypeHelper'),
InputOutputHelper = require('../../../helper/InputOutputHelper');
var utils = require('../../../Utils'),
escapeHTML = utils.escapeHTML,
triggerClickEvent = utils.triggerClickEvent;
var implementationType = require('./implementation/ImplementationType'),
delegate = require('./implementation/Delegate'),
external = require('./implementation/External'),
callable = require('./implementation/Callable'),
resultVariable = require('./implementation/ResultVariable');
var entryFactory = require('../../../factory/EntryFactory');
var domQuery = require('min-dom').query,
domClosest = require('min-dom').closest,
domClasses = require('min-dom').classes;
function getImplementationType(element) {
return ImplementationTypeHelper.getImplementationType(element);
}
function getBusinessObject(element) {
return ImplementationTypeHelper.getServiceTaskLikeBusinessObject(element);
}
function isDmnCapable(element) {
return ImplementationTypeHelper.isDmnCapable(element);
}
function isExternalCapable(element) {
return ImplementationTypeHelper.isExternalCapable(element);
}
function isServiceTaskLike(element) {
return ImplementationTypeHelper.isServiceTaskLike(element);
}
module.exports = function(group, element, bpmnFactory, translate) {
if (!isServiceTaskLike(getBusinessObject(element))) {
return;
}
var hasDmnSupport = isDmnCapable(element);
var hasExternalSupport = isExternalCapable(getBusinessObject(element));
// implementation type ////////////////////////////////////
group.entries = group.entries.concat(implementationType(element, bpmnFactory, {
getBusinessObject: getBusinessObject,
getImplementationType: getImplementationType,
hasDmnSupport: hasDmnSupport,
hasExternalSupport: hasExternalSupport,
hasServiceTaskLikeSupport: true
}, translate));
// delegate (class, expression, delegateExpression) //////////
group.entries = group.entries.concat(delegate(element, bpmnFactory, {
getBusinessObject: getBusinessObject,
getImplementationType: getImplementationType
}, translate));
// result variable /////////////////////////////////////////
group.entries = group.entries.concat(resultVariable(element, bpmnFactory, {
getBusinessObject: getBusinessObject,
getImplementationType: getImplementationType,
hideResultVariable: function(element, node) {
return getImplementationType(element) !== 'expression';
}
}, translate));
// external //////////////////////////////////////////////////
if (hasExternalSupport) {
group.entries = group.entries.concat(external(element, bpmnFactory, {
getBusinessObject: getBusinessObject,
getImplementationType: getImplementationType
}, translate));
}
// dmn ////////////////////////////////////////////////////////
if (hasDmnSupport) {
group.entries = group.entries.concat(callable(element, bpmnFactory, {
getCallableType: getImplementationType
}, translate));
}
// connector ////////////////////////////////////////////////
var isConnector = function(element) {
return getImplementationType(element) === 'connector';
};
group.entries.push(entryFactory.link({
id: 'configureConnectorLink',
label: translate('Configure Connector'),
handleClick: function(element, node, event) {
var connectorTabEl = getTabNode(node, 'connector');
if (connectorTabEl) {
triggerClickEvent(connectorTabEl);
}
// suppress actual link click
return false;
},
showLink: function(element, node) {
var link = domQuery('a', node);
link.textContent = '';
domClasses(link).remove('bpp-error-message');
if (isConnector(element)) {
var connectorId = InputOutputHelper.getConnector(element).get('connectorId');
if (connectorId) {
link.textContent = translate('Configure Connector');
} else {
link.innerHTML = '<span class="bpp-icon-warning"></span> ' + escapeHTML(translate('Must configure Connector'));
domClasses(link).add('bpp-error-message');
}
return true;
}
return false;
}
}));
};
// helpers ///////////////////////////
function getTabNode(el, id) {
var containerEl = domClosest(el, '.bpp-properties-panel');
return domQuery('a[data-tab-target="' + id + '"]', containerEl);
}

@ -0,0 +1,23 @@
'use strict';
var entryFactory = require('../../../factory/EntryFactory'),
is = require('bpmn-js/lib/util/ModelUtil').is,
getBusinessObject = require('bpmn-js/lib/util/ModelUtil').getBusinessObject;
module.exports = function(group, element, translate) {
var bo = getBusinessObject(element);
if (!bo) {
return;
}
if (is(element, 'activiti:Initiator') && !is(element.parent, 'bpmn:SubProcess')) {
group.entries.push(entryFactory.textField({
id: 'initiator',
label: translate('Initiator'),
modelProperty: 'initiator'
}));
}
};

@ -0,0 +1,27 @@
'use strict';
var is = require('bpmn-js/lib/util/ModelUtil').is,
getBusinessObject = require('bpmn-js/lib/util/ModelUtil').getBusinessObject;
var tasklist = require('./implementation/Tasklist');
module.exports = function(group, element, bpmnFactory, translate) {
var businessObject = getBusinessObject(element);
if (is(element, 'activiti:Process') ||
is(element, 'bpmn:Participant') && businessObject.get('processRef')) {
group.entries = group.entries.concat(tasklist(element, bpmnFactory, {
getBusinessObject: function(element) {
var bo = getBusinessObject(element);
if (!is(bo, 'bpmn:Participant')) {
return bo;
}
return bo.get('processRef');
}
}, translate));
}
};

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save