ChangeElementTemplateHandler.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471
  1. 'use strict';
  2. var findExtension = require('../Helper').findExtension,
  3. findExtensions = require('../Helper').findExtensions;
  4. var createActivitiProperty = require('../CreateHelper').createActivitiProperty,
  5. createInputParameter = require('../CreateHelper').createInputParameter,
  6. createOutputParameter = require('../CreateHelper').createOutputParameter,
  7. createActivitiIn = require('../CreateHelper').createActivitiIn,
  8. createActivitiOut = require('../CreateHelper').createActivitiOut,
  9. createActivitiInWithBusinessKey = require('../CreateHelper').createActivitiInWithBusinessKey,
  10. createActivitiExecutionListenerScript = require('../CreateHelper').createActivitiExecutionListenerScript,
  11. createActivitiFieldInjection = require('../CreateHelper').createActivitiFieldInjection;
  12. var forEach = require('lodash/forEach');
  13. var ACTIVITI_SERVICE_TASK_LIKE = [
  14. 'activiti:class',
  15. 'activiti:delegateExpression',
  16. 'activiti:expression'
  17. ];
  18. /**
  19. * A handler that changes the modeling template of a BPMN element.
  20. */
  21. function ChangeElementTemplateHandler(modeling, commandStack, bpmnFactory) {
  22. function getOrCreateExtensionElements(element) {
  23. var bo = element.businessObject;
  24. var extensionElements = bo.extensionElements;
  25. // add extension elements
  26. if (!extensionElements) {
  27. extensionElements = bpmnFactory.create('bpmn:ExtensionElements', {
  28. values: []
  29. });
  30. modeling.updateProperties(element, {
  31. extensionElements: extensionElements
  32. });
  33. }
  34. return extensionElements;
  35. }
  36. function updateModelerTemplate(element, newTemplate) {
  37. modeling.updateProperties(element, {
  38. 'activiti:modelerTemplate': newTemplate && newTemplate.id
  39. });
  40. }
  41. function updateIoMappings(element, newTemplate, context) {
  42. var newMappings = createInputOutputMappings(newTemplate, bpmnFactory),
  43. oldMappings;
  44. if (!newMappings) {
  45. return;
  46. }
  47. if (context) {
  48. commandStack.execute('properties-panel.update-businessobject', {
  49. element: element,
  50. businessObject: context,
  51. properties: { inputOutput: newMappings }
  52. });
  53. } else {
  54. context = getOrCreateExtensionElements(element);
  55. oldMappings = findExtension(element, 'activiti:InputOutput');
  56. commandStack.execute('properties-panel.update-businessobject-list', {
  57. element: element,
  58. currentObject: context,
  59. propertyName: 'values',
  60. objectsToAdd: [ newMappings ],
  61. objectsToRemove: oldMappings ? [ oldMappings ] : []
  62. });
  63. }
  64. }
  65. function updateActivitiField(element, newTemplate, context) {
  66. var newMappings = createActivitiFieldInjections(newTemplate, bpmnFactory),
  67. oldMappings;
  68. if (!newMappings) {
  69. return;
  70. }
  71. if (context) {
  72. commandStack.execute('properties-panel.update-businessobject', {
  73. element: element,
  74. businessObject: context,
  75. properties: { field: newMappings }
  76. });
  77. } else {
  78. context = getOrCreateExtensionElements(element);
  79. oldMappings = findExtensions(element, ['activiti:Field']);
  80. commandStack.execute('properties-panel.update-businessobject-list', {
  81. element: element,
  82. currentObject: context,
  83. propertyName: 'values',
  84. objectsToAdd: newMappings,
  85. objectsToRemove: oldMappings ? oldMappings : []
  86. });
  87. }
  88. }
  89. function updateActivitiProperties(element, newTemplate, context) {
  90. var newProperties = createActivitiProperties(newTemplate, bpmnFactory),
  91. oldProperties;
  92. if (!newProperties) {
  93. return;
  94. }
  95. if (context) {
  96. commandStack.execute('properties-panel.update-businessobject', {
  97. element: element,
  98. businessObject: context,
  99. properties: { properties: newProperties }
  100. });
  101. } else {
  102. context = getOrCreateExtensionElements(element);
  103. oldProperties = findExtension(element, 'activiti:Properties');
  104. commandStack.execute('properties-panel.update-businessobject-list', {
  105. element: element,
  106. currentObject: context,
  107. propertyName: 'values',
  108. objectsToAdd: [ newProperties ],
  109. objectsToRemove: oldProperties ? [ oldProperties ] : []
  110. });
  111. }
  112. }
  113. function updateProperties(element, newTemplate, context) {
  114. var newProperties = createBpmnPropertyUpdates(newTemplate, bpmnFactory);
  115. var newPropertiesCount = Object.keys(newProperties).length;
  116. if (!newPropertiesCount) {
  117. return;
  118. }
  119. if (context) {
  120. commandStack.execute('properties-panel.update-businessobject', {
  121. element: element,
  122. businessObject: context,
  123. properties: newProperties
  124. });
  125. } else {
  126. modeling.updateProperties(element, newProperties);
  127. }
  128. }
  129. function updateInOut(element, newTemplate, context) {
  130. var newInOut = createActivitiInOut(newTemplate, bpmnFactory),
  131. oldInOut;
  132. if (!newInOut) {
  133. return;
  134. }
  135. if (context) {
  136. commandStack.execute('properties-panel.update-businessobject', {
  137. element: element,
  138. businessObject: context,
  139. properties: { inout: newInOut }
  140. });
  141. } else {
  142. context = getOrCreateExtensionElements(element);
  143. oldInOut = findExtensions(context, [ 'activiti:In', 'activiti:Out' ]);
  144. commandStack.execute('properties-panel.update-businessobject-list', {
  145. element: element,
  146. currentObject: context,
  147. propertyName: 'values',
  148. objectsToAdd: newInOut,
  149. objectsToRemove: oldInOut
  150. });
  151. }
  152. }
  153. function updateExecutionListener(element, newTemplate, context) {
  154. var newExecutionListeners = createActivitiExecutionListeners(newTemplate, bpmnFactory),
  155. oldExecutionsListeners;
  156. if (!newExecutionListeners.length) {
  157. return;
  158. }
  159. if (context) {
  160. commandStack.execute('properties-panel.update-businessobject', {
  161. element: element,
  162. businessObject: context,
  163. properties: { executionListener: newExecutionListeners }
  164. });
  165. } else {
  166. context = getOrCreateExtensionElements(element);
  167. oldExecutionsListeners = findExtensions(context, [ 'activiti:ExecutionListener' ]);
  168. commandStack.execute('properties-panel.update-businessobject-list', {
  169. element: element,
  170. currentObject: context,
  171. propertyName: 'values',
  172. objectsToAdd: newExecutionListeners,
  173. objectsToRemove: oldExecutionsListeners
  174. });
  175. }
  176. }
  177. /**
  178. * Update / recreate a scoped element.
  179. *
  180. * @param {djs.model.Base} element the diagram parent element
  181. * @param {String} scopeName name of the scope, i.e. activiti:Connector
  182. * @param {Object} scopeDefinition
  183. */
  184. function updateScopeElements(element, scopeName, scopeDefinition) {
  185. var scopeElement = bpmnFactory.create(scopeName);
  186. // update activiti:inputOutput
  187. updateIoMappings(element, scopeDefinition, scopeElement);
  188. // update activiti:field
  189. updateActivitiField(element, scopeDefinition, scopeElement);
  190. // update activiti:properties
  191. updateActivitiProperties(element, scopeDefinition, scopeElement);
  192. // update other properties (bpmn:condition, activiti:async, ...)
  193. updateProperties(element, scopeDefinition, scopeElement);
  194. // update activiti:in and activiti:out
  195. updateInOut(element, scopeDefinition, scopeElement);
  196. // update activiti:executionListener
  197. updateExecutionListener(element, scopeDefinition, scopeElement);
  198. var extensionElements = getOrCreateExtensionElements(element);
  199. var oldScope = findExtension(extensionElements, scopeName);
  200. commandStack.execute('properties-panel.update-businessobject-list', {
  201. element: element,
  202. currentObject: extensionElements,
  203. propertyName: 'values',
  204. objectsToAdd: [ scopeElement ],
  205. objectsToRemove: oldScope ? [ oldScope ] : []
  206. });
  207. }
  208. /**
  209. * Compose an element template change action, updating all
  210. * necessary underlying properties.
  211. *
  212. * @param {Object} context
  213. * @param {Object} context.element
  214. * @param {Object} context.oldTemplate
  215. * @param {Object} context.newTemplate
  216. */
  217. this.preExecute = function(context) {
  218. var element = context.element,
  219. newTemplate = context.newTemplate;
  220. // update activiti:modelerTemplate attribute
  221. updateModelerTemplate(element, newTemplate);
  222. if (newTemplate) {
  223. // update activiti:inputOutput
  224. updateIoMappings(element, newTemplate);
  225. // update activiti:field
  226. updateActivitiField(element, newTemplate);
  227. // update activiti:properties
  228. updateActivitiProperties(element, newTemplate);
  229. // update other properties (bpmn:condition, activiti:async, ...)
  230. updateProperties(element, newTemplate);
  231. // update activiti:in and activiti:out
  232. updateInOut(element, newTemplate);
  233. // update activiti:executionListener
  234. updateExecutionListener(element, newTemplate);
  235. // loop on scopes properties
  236. forEach(newTemplate.scopes, function(scopeDefinition, scopeName) {
  237. updateScopeElements(element, scopeName, scopeDefinition);
  238. });
  239. }
  240. };
  241. }
  242. ChangeElementTemplateHandler.$inject = [ 'modeling', 'commandStack', 'bpmnFactory' ];
  243. module.exports = ChangeElementTemplateHandler;
  244. // helpers /////////////////////////////
  245. function createBpmnPropertyUpdates(template, bpmnFactory) {
  246. var propertyUpdates = {};
  247. template.properties.forEach(function(p) {
  248. var binding = p.binding,
  249. bindingTarget = binding.name,
  250. propertyValue;
  251. if (binding.type === 'property') {
  252. if (bindingTarget === 'conditionExpression') {
  253. propertyValue = bpmnFactory.create('bpmn:FormalExpression', {
  254. body: p.value,
  255. language: binding.scriptFormat
  256. });
  257. } else {
  258. propertyValue = p.value;
  259. }
  260. // assigning activiti:async to true|false
  261. // assigning bpmn:conditionExpression to { $type: 'bpmn:FormalExpression', ... }
  262. propertyUpdates[bindingTarget] = propertyValue;
  263. // make sure we unset other "implementation types"
  264. // when applying a activiti:class template onto a preconfigured
  265. // activiti:delegateExpression element
  266. if (ACTIVITI_SERVICE_TASK_LIKE.indexOf(bindingTarget) !== -1) {
  267. ACTIVITI_SERVICE_TASK_LIKE.forEach(function(prop) {
  268. if (prop !== bindingTarget) {
  269. propertyUpdates[prop] = undefined;
  270. }
  271. });
  272. }
  273. }
  274. });
  275. return propertyUpdates;
  276. }
  277. function createActivitiFieldInjections(template, bpmnFactory) {
  278. var injections = [];
  279. template.properties.forEach(function(p) {
  280. var binding = p.binding,
  281. bindingType = binding.type;
  282. if (bindingType === 'activiti:field') {
  283. injections.push(createActivitiFieldInjection(
  284. binding, p.value, bpmnFactory
  285. ));
  286. }
  287. });
  288. if (injections.length) {
  289. return injections;
  290. }
  291. }
  292. function createActivitiProperties(template, bpmnFactory) {
  293. var properties = [];
  294. template.properties.forEach(function(p) {
  295. var binding = p.binding,
  296. bindingType = binding.type;
  297. if (bindingType === 'activiti:property') {
  298. properties.push(createActivitiProperty(
  299. binding, p.value, bpmnFactory
  300. ));
  301. }
  302. });
  303. if (properties.length) {
  304. return bpmnFactory.create('activiti:Properties', {
  305. values: properties
  306. });
  307. }
  308. }
  309. function createInputOutputMappings(template, bpmnFactory) {
  310. var inputParameters = [],
  311. outputParameters = [];
  312. template.properties.forEach(function(p) {
  313. var binding = p.binding,
  314. bindingType = binding.type;
  315. if (bindingType === 'activiti:inputParameter') {
  316. inputParameters.push(createInputParameter(
  317. binding, p.value, bpmnFactory
  318. ));
  319. }
  320. if (bindingType === 'activiti:outputParameter') {
  321. outputParameters.push(createOutputParameter(
  322. binding, p.value, bpmnFactory
  323. ));
  324. }
  325. });
  326. // do we need to create new ioMappings (?)
  327. if (outputParameters.length || inputParameters.length) {
  328. return bpmnFactory.create('activiti:InputOutput', {
  329. inputParameters: inputParameters,
  330. outputParameters: outputParameters
  331. });
  332. }
  333. }
  334. function createActivitiInOut(template, bpmnFactory) {
  335. var inOuts = [];
  336. template.properties.forEach(function(p) {
  337. var binding = p.binding,
  338. bindingType = binding.type;
  339. if (bindingType === 'activiti:in') {
  340. inOuts.push(createActivitiIn(
  341. binding, p.value, bpmnFactory
  342. ));
  343. } else
  344. if (bindingType === 'activiti:out') {
  345. inOuts.push(createActivitiOut(
  346. binding, p.value, bpmnFactory
  347. ));
  348. } else
  349. if (bindingType === 'activiti:in:businessKey') {
  350. inOuts.push(createActivitiInWithBusinessKey(
  351. binding, p.value, bpmnFactory
  352. ));
  353. }
  354. });
  355. return inOuts;
  356. }
  357. function createActivitiExecutionListeners(template, bpmnFactory) {
  358. var executionListener = [];
  359. template.properties.forEach(function(p) {
  360. var binding = p.binding,
  361. bindingType = binding.type;
  362. if (bindingType === 'activiti:executionListener') {
  363. executionListener.push(createActivitiExecutionListenerScript(
  364. binding, p.value, bpmnFactory
  365. ));
  366. }
  367. });
  368. return executionListener;
  369. }