Hello all,
I have been struggling to implement a custom element. Specifically I am trying to mimic the Email Task that is available in flowable.
For reference it is a modified service task with xml code that looks like this
<serviceTask id="email task id" name="email task name" flowable:type="mail">
<extensionElements>
<flowable:field name="headers">
<flowable:string>
<![CDATA[ headerField ]]>
</flowable:string>
</flowable:field>
<flowable:field name="to">
<flowable:string>
<![CDATA[ tofield@gmail.com ]]>
</flowable:string>
</flowable:field>
<flowable:field name="from">
<flowable:string>
<![CDATA[ fromField@gmail.com ]]>
</flowable:string>
</flowable:field>
<flowable:field name="subject">
<flowable:string>
<![CDATA[ subjectField ]]>
</flowable:string>
</flowable:field>
<flowable:field name="cc">
<flowable:string>
<![CDATA[ ccField ]]>
</flowable:string>
</flowable:field>
<flowable:field name="bcc">
<flowable:string>
<![CDATA[ bccField ]]>
</flowable:string>
</flowable:field>
<flowable:field name="text">
<flowable:string>
<![CDATA[ textField body of email? ]]>
</flowable:string>
</flowable:field>
<flowable:field name="html">
<flowable:string>
<![CDATA[ htmlField ]]>
</flowable:string>
</flowable:field>
<flowable:field name="htmlVar">
<flowable:string>
<![CDATA[ htmlVar ]]>
</flowable:string>
</flowable:field>
<flowable:field name="textVar">
<flowable:string>
<![CDATA[ textVar ]]>
</flowable:string>
</flowable:field>
</extensionElements>
</serviceTask>
I am coding within angular and would like to keep all my files to typescript if possible. I have been struggling to get the email task to be separated from the service task, the closest I have gotten so far is getting the “Email Task” to appear within the replacement options of the popup menu when clicking on the wrench icon on a task element. But after I click on the Email Task type, it is then replaced by a service task. This is probably due to something I have wrong in my custom replace menu provider (my code below).
import { assign } from 'lodash';
export default class CustomReplaceMenuProvider {
private bpmnReplace: any;
private popupMenu: any;
private modeling: any;
private bpmnFactory: any;
private rules: any;
private translate: any;
constructor(bpmnReplace, popupMenu, modeling, bpmnFactory, rules, translate) {
this.bpmnReplace = bpmnReplace;
this.popupMenu = popupMenu;
this.modeling = modeling;
this.bpmnFactory = bpmnFactory;
this.rules = rules;
this.translate = translate;
this.popupMenu.registerProvider("bpmn-replace", this);
}
getPopupMenuEntries(element) {
const entries = {};
if (element.type === "bpmn:Task") {
entries["replace-with-email-task"] = {
label: "Email Task",
className: "bpmn-icon-receive",
action: () => {
this.replaceElement(element, "bpmn:ServiceTask", { custom: "EmailTask" });
},
};
}
return entries;
}
replaceElement(element, _newType, customOptions) {
const businessObject = this.bpmnFactory.create("bpmn:ServiceTask");
this.modeling.updateProperties(element, {
"custom:emailSubject": "Default Subject",
"custom:recipient": "recipient@example.com",
"custom:emailBody": "Test body text"
});
const newElement = this.bpmnReplace.replaceElement(
element,
assign(
{ type: "bpmn:ServiceTask", businessObject },
customOptions
)
);
return newElement;
}
}
(CustomReplaceMenuProvider as any).$inject = [
"bpmnReplace",
"popupMenu",
"modeling",
"bpmnFactory",
"rules",
"translate",
];
Here is my moddle extension of this new Email Task
{
"name": "EmailTask",
"uri": "http://custom-bpmn/schema/email-task",
"prefix": "custom",
"types": [
{
"name": "EmailTask",
"superClass": ["bpmn:Task"],
"properties": [
{
"name": "emailSubject",
"isAttr": true,
"type": "String"
},
{
"name": "emailBody",
"isAttr": true,
"type": "String"
},
{
"name": "recipient",
"isAttr": true,
"type": "String"
}
]
}
]
}
My index.ts
import CustomReplaceMenuProvider from "./CustomReplaceMenuProvider";
export default {
__depends__: ["popupMenu", "bpmnReplace", "modeling", "bpmnFactory"],
__init__: ["customReplaceMenuProvider"],
customReplaceMenuProvider: ["type", CustomReplaceMenuProvider]
}
and this is where i declare my bpmnjs in my main file
bpmnJS: BpmnJS = new BpmnJS({
additionalModules: [
BpmnPropertiesPanelModule,
BpmnPropertiesProviderModule,
CustomReplaceModule
],
moddleExtensions: {
custom: emailTask
}
});
Currently after exporting my bpmn while using this configuration my “Email Task” xml looks like this:
<serviceTask id="Activity_15olasd" name="ServiceTask 140652" flowable:servicetasktriggerable="false" flowable:servicetaskUseLocalScopeForResultVariable="false" flowable:class="class" flowable:servicetaskstoreresultvariabletransient="false"/>
And as you can see my attributes provided in my custom moddle are missing. So I am not sure what I doing wrong at the moment.
I would provide a codeSandbox but this is for a fairly large project and I can not publicly distribute our code ATM.
I have also looked at a lot of the bpmn.io examples and the closest one would be the Custom Element example, but this is a bit different to what I want since I would like to have a custom properties panel for filling out the required fields for my email task. I also do not have diagram-js imported for a custom renderer and would prefer to not have to add more imports than necessary.
I am assuming I would need to do a combination of the custom element example and the properties panel extension example. But I have been really struggling to wrap my head around what all is needed and what all needs to be changed.
Any help and/or guidance would be greatly appreciated,
Thank you!