diff --git a/lib/features/modeling/behavior/ModelingFeedback.js b/lib/features/modeling/behavior/ModelingFeedback.js
index 762952a6..130390a7 100644
--- a/lib/features/modeling/behavior/ModelingFeedback.js
+++ b/lib/features/modeling/behavior/ModelingFeedback.js
@@ -2,18 +2,19 @@
var is = require('../../../util/ModelUtil').is;
-var COLLAB_ERR_MSG = 'flow elements must be children of pools/participants';
+var COLLAB_ERR_MSG = 'flow elements must be children of pools/participants',
+ PROCESS_ERR_MSG = 'participants cannot be pasted onto a non-empty process diagram';
function ModelingFeedback(eventBus, tooltips, translate) {
- function showError(position, message) {
+ function showError(position, message, timeout) {
tooltips.add({
position: {
x: position.x + 5,
y: position.y + 5
},
type: 'error',
- timeout: 2000,
+ timeout: timeout || 2000,
html: '
' + message + '
'
});
}
@@ -36,6 +37,10 @@ function ModelingFeedback(eventBus, tooltips, translate) {
if (is(target, 'bpmn:Collaboration')) {
showError(position, translate(COLLAB_ERR_MSG));
}
+
+ if (is(target, 'bpmn:Process')) {
+ showError(position, translate(PROCESS_ERR_MSG), 3000);
+ }
});
}
diff --git a/lib/features/rules/BpmnRules.js b/lib/features/rules/BpmnRules.js
index bc8fd527..f57dc239 100644
--- a/lib/features/rules/BpmnRules.js
+++ b/lib/features/rules/BpmnRules.js
@@ -427,7 +427,8 @@ function canDrop(element, target, position) {
}
function canPaste(tree, target) {
- var topLevel = tree[0];
+ var topLevel = tree[0],
+ participants;
if (is(target, 'bpmn:Collaboration')) {
return every(topLevel, function(e) {
@@ -435,6 +436,14 @@ function canPaste(tree, target) {
});
}
+ if (is(target, 'bpmn:Process')) {
+ participants = any(topLevel, function(e) {
+ return e.type === 'bpmn:Participant';
+ });
+
+ return !(participants && target.children.length > 0);
+ }
+
// disallow to create elements on collapsed pools
if (is(target, 'bpmn:Participant') && !isExpanded(target)) {
return false;
diff --git a/test/spec/features/copy-paste/BpmnCopyPasteSpec.js b/test/spec/features/copy-paste/BpmnCopyPasteSpec.js
index 26f6ad5c..94b9ea6a 100644
--- a/test/spec/features/copy-paste/BpmnCopyPasteSpec.js
+++ b/test/spec/features/copy-paste/BpmnCopyPasteSpec.js
@@ -48,7 +48,7 @@ describe('features/copy-paste', function() {
collaborationMultipleXML = require('../../../fixtures/bpmn/features/copy-paste/collaboration-multiple.bpmn');
- var integrationTest = function(ids) {
+ function integrationTest(ids) {
return function(canvas, elementRegistry, modeling, copyPaste, commandStack) {
// given
var shapes = elementRegistry.getAll(),
@@ -127,280 +127,320 @@ describe('features/copy-paste', function() {
};
};
- describe('basic diagram', function() {
- beforeEach(bootstrapModeler(basicXML, { modules: testModules }));
+ describe('basic diagram', function() {
- describe('copy', function() {
+ beforeEach(bootstrapModeler(basicXML, { modules: testModules }));
- it('selected elements', inject(function(elementRegistry, copyPaste) {
+ describe('copy', function() {
- // given
- var subProcess, startEvent, boundaryEvent, textAnnotation, conditionalFlow, defaultFlow,
- tree;
+ it('selected elements', inject(function(elementRegistry, copyPaste) {
- // when
- copyPaste.copy(elementRegistry.get('SubProcess_1kd6ist'));
+ // given
+ var subProcess, startEvent, boundaryEvent, textAnnotation, conditionalFlow, defaultFlow,
+ tree;
- tree = new DescriptorTree(copyPaste._tree);
+ // when
+ copyPaste.copy(elementRegistry.get('SubProcess_1kd6ist'));
- startEvent = tree.getElement('StartEvent_1');
- boundaryEvent = tree.getElement('BoundaryEvent_1c94bi9');
- subProcess = tree.getElement('SubProcess_1kd6ist');
- textAnnotation = tree.getElement('TextAnnotation_0h1hhgg');
- conditionalFlow = tree.getElement('SequenceFlow_07vo2r8');
- defaultFlow = tree.getElement('Task_1fo63a7');
+ tree = new DescriptorTree(copyPaste._tree);
- // then
- expect(tree.getLength()).to.equal(3);
+ startEvent = tree.getElement('StartEvent_1');
+ boundaryEvent = tree.getElement('BoundaryEvent_1c94bi9');
+ subProcess = tree.getElement('SubProcess_1kd6ist');
+ textAnnotation = tree.getElement('TextAnnotation_0h1hhgg');
+ conditionalFlow = tree.getElement('SequenceFlow_07vo2r8');
+ defaultFlow = tree.getElement('Task_1fo63a7');
- expect(tree.getDepthLength(0)).to.equal(1);
- expect(tree.getDepthLength(1)).to.equal(3);
- expect(tree.getDepthLength(2)).to.equal(15);
+ // then
+ expect(tree.getLength()).to.equal(3);
- expect(subProcess.isExpanded).to.be.true;
- expect(startEvent.name).to.equal('hello');
- expect(textAnnotation.text).to.equal('foo');
- expect(boundaryEvent.eventDefinitions).to.contain('bpmn:TimerEventDefinition');
- }));
+ expect(tree.getDepthLength(0)).to.equal(1);
+ expect(tree.getDepthLength(1)).to.equal(3);
+ expect(tree.getDepthLength(2)).to.equal(15);
- });
-
- describe('integration', function() {
-
- it('should retain label\'s relative position',
- inject(function(modeling, copyPaste, canvas, elementRegistry) {
- // given
- var startEvent = elementRegistry.get('StartEvent_1'),
- startEventLabel = startEvent.label,
- seqFlow = elementRegistry.get('SequenceFlow_1rtr33r'),
- seqFlowLabel = seqFlow.label,
- task = elementRegistry.get('Task_1fo63a7'),
- rootElement = canvas.getRootElement(),
- newStrtEvt, newSeqFlow;
-
- // when
- copyPaste.copy([ startEvent, task ]);
-
- copyPaste.paste({
- element: rootElement,
- point: {
- x: 1100,
- y: 250
- }
- });
-
- newStrtEvt = elementRegistry.filter(function(element) {
- return element.parent === rootElement && element.type === 'bpmn:StartEvent';
- })[0];
-
- newSeqFlow = elementRegistry.filter(function(element) {
- return element.parent === rootElement && element.type === 'bpmn:SequenceFlow';
- })[0];
-
- // then
- expect(newStrtEvt.label.x - newStrtEvt.x).to.equal(startEventLabel.x - startEvent.x);
- expect(newStrtEvt.label.y - newStrtEvt.y).to.equal(startEventLabel.y - startEvent.y);
-
- expect(newSeqFlow.label.x - newSeqFlow.waypoints[0].x).to.equal(seqFlowLabel.x - seqFlow.waypoints[0].x);
- expect(newSeqFlow.label.y - newSeqFlow.waypoints[0].y).to.equal(seqFlowLabel.y - seqFlow.waypoints[0].y);
- }));
-
-
- it('should retain default & conditional flow property',
- inject(function(elementRegistry, copyPaste, canvas, modeling) {
- // given
- var subProcess = elementRegistry.get('SubProcess_1kd6ist'),
- rootElement = canvas.getRootElement(),
- task, defaultFlow, conditionalFlow;
-
- // when
- copyPaste.copy(subProcess);
-
- modeling.removeElements([ subProcess ]);
-
- copyPaste.paste({
- element: rootElement,
- point: {
- x: 1100,
- y: 250
- }
- });
-
- task = elementRegistry.filter(function(element) {
- return element.type === 'bpmn:Task';
- })[0];
-
- defaultFlow = elementRegistry.filter(function(element) {
- return !!(element.type === 'bpmn:SequenceFlow' && task.businessObject.default.id === element.id);
- })[0];
-
- conditionalFlow = elementRegistry.filter(function(element) {
- return !!(element.type === 'bpmn:SequenceFlow' && element.businessObject.conditionExpression);
- })[0];
-
- expect(defaultFlow).to.exist;
- expect(conditionalFlow).to.exist;
- }));
-
-
- it('should retain loop characteristics',
- inject(function(elementRegistry, copyPaste, canvas, modeling) {
- // given
- var subProcess = elementRegistry.get('SubProcess_0gev7mx'),
- rootElement = canvas.getRootElement(),
- loopCharacteristics;
-
- // when
- copyPaste.copy(subProcess);
-
- modeling.removeElements([ subProcess ]);
-
- copyPaste.paste({
- element: rootElement,
- point: {
- x: 1100,
- y: 250
- }
- });
-
- subProcess = elementRegistry.filter(function(element) {
- return !!(element.id !== 'SubProcess_1kd6ist' && element.type === 'bpmn:SubProcess');
- })[0];
-
- loopCharacteristics = subProcess.businessObject.loopCharacteristics;
-
- expect(loopCharacteristics.$type).to.equal('bpmn:MultiInstanceLoopCharacteristics');
- expect(loopCharacteristics.isSequential).to.be.true;
- }));
-
-
- it('selected elements', inject(integrationTest([ 'SubProcess_1kd6ist' ])));
-
- });
-
- describe('rules', function() {
-
- it('disallow individual boundary events copying', inject(function(copyPaste, elementRegistry, canvas) {
-
- var boundaryEventA = elementRegistry.get('BoundaryEvent_1404oxd'),
- boundaryEventB = elementRegistry.get('BoundaryEvent_1c94bi9'),
- tree;
-
- // when
- copyPaste.copy([ boundaryEventA, boundaryEventB ]);
-
- tree = new DescriptorTree(copyPaste._tree);
-
- expect(tree.getLength()).to.equal(0);
- }));
- });
+ expect(subProcess.isExpanded).to.be.true;
+ expect(startEvent.name).to.equal('hello');
+ expect(textAnnotation.text).to.equal('foo');
+ expect(boundaryEvent.eventDefinitions).to.contain('bpmn:TimerEventDefinition');
+ }));
});
- describe('basic collaboration', function() {
- beforeEach(bootstrapModeler(collaborationXML, { modules: testModules }));
+ describe('integration', function() {
- describe('integration', function() {
+ it('should retain label\'s relative position',
+ inject(function(modeling, copyPaste, canvas, elementRegistry) {
+ // given
+ var startEvent = elementRegistry.get('StartEvent_1'),
+ startEventLabel = startEvent.label,
+ seqFlow = elementRegistry.get('SequenceFlow_1rtr33r'),
+ seqFlowLabel = seqFlow.label,
+ task = elementRegistry.get('Task_1fo63a7'),
+ rootElement = canvas.getRootElement(),
+ newStrtEvt, newSeqFlow;
- it('participant with including lanes + elements', inject(integrationTest([ 'Participant_0uu1rvj' ])));
+ // when
+ copyPaste.copy([ startEvent, task ]);
- it('collapsed pool', inject(integrationTest([ 'Participant_145muai' ])));
+ copyPaste.paste({
+ element: rootElement,
+ point: {
+ x: 1100,
+ y: 250
+ }
+ });
- });
+ newStrtEvt = elementRegistry.filter(function(element) {
+ return element.parent === rootElement && element.type === 'bpmn:StartEvent';
+ })[0];
- describe('rules', function () {
+ newSeqFlow = elementRegistry.filter(function(element) {
+ return element.parent === rootElement && element.type === 'bpmn:SequenceFlow';
+ })[0];
- it('disallow individual lanes copying', inject(function(copyPaste, elementRegistry, canvas) {
+ // then
+ expect(newStrtEvt.label.x - newStrtEvt.x).to.equal(startEventLabel.x - startEvent.x);
+ expect(newStrtEvt.label.y - newStrtEvt.y).to.equal(startEventLabel.y - startEvent.y);
- var laneA = elementRegistry.get('Lane_13h648l'),
- laneB = elementRegistry.get('Lane_1gl63sa'),
- tree;
-
- // when
- copyPaste.copy([ laneA, laneB ]);
-
- tree = new DescriptorTree(copyPaste._tree);
-
- expect(tree.getLength()).to.equal(0);
- }));
+ expect(newSeqFlow.label.x - newSeqFlow.waypoints[0].x).to.equal(seqFlowLabel.x - seqFlow.waypoints[0].x);
+ expect(newSeqFlow.label.y - newSeqFlow.waypoints[0].y).to.equal(seqFlowLabel.y - seqFlow.waypoints[0].y);
+ }));
- it('pasting on a collaboration is disallowed when NOT every element is a Participant',
- inject(function(copyPaste, elementRegistry, canvas, tooltips, eventBus) {
- var task = elementRegistry.get('Task_13xbgyg'),
- participant = elementRegistry.get('Participant_145muai'),
- collaboration = canvas.getRootElement(),
- tree;
+ it('should retain default & conditional flow property',
+ inject(function(elementRegistry, copyPaste, canvas, modeling) {
+ // given
+ var subProcess = elementRegistry.get('SubProcess_1kd6ist'),
+ rootElement = canvas.getRootElement(),
+ task, defaultFlow, conditionalFlow;
- var pasteRejected = sinon.spy(function() {});
+ // when
+ copyPaste.copy(subProcess);
- // when
- copyPaste.copy([ task, participant ]);
+ modeling.removeElements([ subProcess ]);
- tree = new DescriptorTree(copyPaste._tree);
+ copyPaste.paste({
+ element: rootElement,
+ point: {
+ x: 1100,
+ y: 250
+ }
+ });
- // then
- expect(tree.getDepthLength(0)).to.equal(2);
+ task = elementRegistry.filter(function(element) {
+ return element.type === 'bpmn:Task';
+ })[0];
- // when
- eventBus.on('elements.paste.rejected', pasteRejected);
+ defaultFlow = elementRegistry.filter(function(element) {
+ return !!(element.type === 'bpmn:SequenceFlow' && task.businessObject.default.id === element.id);
+ })[0];
- copyPaste.paste({
- element: collaboration,
- point: {
- x: 1000,
- y: 1000
- }
- });
+ conditionalFlow = elementRegistry.filter(function(element) {
+ return !!(element.type === 'bpmn:SequenceFlow' && element.businessObject.conditionExpression);
+ })[0];
- expect(pasteRejected).to.have.been.called;
- }));
+ expect(defaultFlow).to.exist;
+ expect(conditionalFlow).to.exist;
+ }));
- });
+
+ it('should retain loop characteristics',
+ inject(function(elementRegistry, copyPaste, canvas, modeling) {
+ // given
+ var subProcess = elementRegistry.get('SubProcess_0gev7mx'),
+ rootElement = canvas.getRootElement(),
+ loopCharacteristics;
+
+ // when
+ copyPaste.copy(subProcess);
+
+ modeling.removeElements([ subProcess ]);
+
+ copyPaste.paste({
+ element: rootElement,
+ point: {
+ x: 1100,
+ y: 250
+ }
+ });
+
+ subProcess = elementRegistry.filter(function(element) {
+ return !!(element.id !== 'SubProcess_1kd6ist' && element.type === 'bpmn:SubProcess');
+ })[0];
+
+ loopCharacteristics = subProcess.businessObject.loopCharacteristics;
+
+ expect(loopCharacteristics.$type).to.equal('bpmn:MultiInstanceLoopCharacteristics');
+ expect(loopCharacteristics.isSequential).to.be.true;
+ }));
+
+
+ it('selected elements', inject(integrationTest([ 'SubProcess_1kd6ist' ])));
});
- describe('complex collaboration', function() {
- beforeEach(bootstrapModeler(collaborationMultipleXML, { modules: testModules }));
+ describe('rules', function() {
- describe('basics', function() {
+ it('disallow individual boundary events copying', inject(function(copyPaste, elementRegistry, canvas) {
- it('pasting on lane', inject(function(elementRegistry, copyPaste) {
- // given
- var lane = elementRegistry.get('Lane_1yo0kyz'),
- task = elementRegistry.get('Task_0n0k2nj'),
- participant = elementRegistry.get('Participant_0pgdgt4');
+ var boundaryEventA = elementRegistry.get('BoundaryEvent_1404oxd'),
+ boundaryEventB = elementRegistry.get('BoundaryEvent_1c94bi9'),
+ tree;
- // when
- copyPaste.copy(task);
+ // when
+ copyPaste.copy([ boundaryEventA, boundaryEventB ]);
- copyPaste.paste({
- element: lane,
- point: {
- x: 200,
- y: 75
- }
- });
+ tree = new DescriptorTree(copyPaste._tree);
- // then
- expect(lane.children).to.be.empty;
- expect(lane.businessObject.flowNodeRef).to.have.length(2);
+ expect(tree.getLength()).to.equal(0);
+ }));
+ });
- expect(lane.parent.children).to.have.length(2);
- expect(participant.children).to.have.length(5);
- }));
+ });
- });
- describe('integration', function() {
+ describe('basic collaboration', function() {
- it('multiple participants', inject(integrationTest([ 'Participant_0pgdgt4', 'Participant_1id96b4' ])));
+ beforeEach(bootstrapModeler(collaborationXML, { modules: testModules }));
- });
+ describe('integration', function() {
+
+ it('participant with including lanes + elements', inject(integrationTest([ 'Participant_0uu1rvj' ])));
+
+ it('collapsed pool', inject(integrationTest([ 'Participant_145muai' ])));
});
+
+ describe('rules', function () {
+
+ it('disallow individual lanes copying', inject(function(copyPaste, elementRegistry, canvas) {
+
+ var laneA = elementRegistry.get('Lane_13h648l'),
+ laneB = elementRegistry.get('Lane_1gl63sa'),
+ tree;
+
+ // when
+ copyPaste.copy([ laneA, laneB ]);
+
+ tree = new DescriptorTree(copyPaste._tree);
+
+ expect(tree.getLength()).to.equal(0);
+ }));
+
+
+ it('pasting on a collaboration is disallowed when NOT every element is a Participant',
+ inject(function(copyPaste, elementRegistry, canvas, tooltips, eventBus) {
+ var task = elementRegistry.get('Task_13xbgyg'),
+ participant = elementRegistry.get('Participant_145muai'),
+ collaboration = canvas.getRootElement(),
+ tree;
+
+ var pasteRejected = sinon.spy(function() {});
+
+ // when
+ copyPaste.copy([ task, participant ]);
+
+ tree = new DescriptorTree(copyPaste._tree);
+
+ // then
+ expect(tree.getDepthLength(0)).to.equal(2);
+
+ // when
+ eventBus.on('elements.paste.rejected', pasteRejected);
+
+ copyPaste.paste({
+ element: collaboration,
+ point: {
+ x: 1000,
+ y: 1000
+ }
+ });
+
+ expect(pasteRejected).to.have.been.called;
+ }));
+
+
+ it('pasting participants on a process is disallowed when it\'s not a collaboration',
+ inject(function(copyPaste, elementRegistry, canvas, tooltips, eventBus, modeling, elementFactory) {
+
+ var participant = elementRegistry.get('Participant_145muai'),
+ otherParticipant = elementRegistry.get('Participant_0uu1rvj'),
+ startEvent = elementFactory.create('shape', { type: 'bpmn:StartEvent' }),
+ rootElement;
+
+ var pasteRejected = sinon.spy(function() {});
+
+ // when
+ copyPaste.copy([ participant ]);
+
+ modeling.removeElements([ participant, otherParticipant ]);
+
+ rootElement = canvas.getRootElement();
+
+ modeling.createShape(startEvent, { x: 50, y: 50 }, rootElement);
+
+ eventBus.on('elements.paste.rejected', pasteRejected);
+
+ copyPaste.paste({
+ element: rootElement,
+ point: {
+ x: 500,
+ y: 200
+ }
+ });
+
+ expect(pasteRejected).to.have.been.called;
+ }));
+
+ });
+
+ });
+
+
+ describe('complex collaboration', function() {
+
+ beforeEach(bootstrapModeler(collaborationMultipleXML, { modules: testModules }));
+
+ describe('basics', function() {
+
+ it('pasting on lane', inject(function(elementRegistry, copyPaste) {
+ // given
+ var lane = elementRegistry.get('Lane_1yo0kyz'),
+ task = elementRegistry.get('Task_0n0k2nj'),
+ participant = elementRegistry.get('Participant_0pgdgt4');
+
+ // when
+ copyPaste.copy(task);
+
+ copyPaste.paste({
+ element: lane,
+ point: {
+ x: 200,
+ y: 75
+ }
+ });
+
+ // then
+ expect(lane.children).to.be.empty;
+ expect(lane.businessObject.flowNodeRef).to.have.length(2);
+
+ expect(lane.parent.children).to.have.length(2);
+ expect(participant.children).to.have.length(5);
+ }));
+
+ });
+
+
+ describe('integration', function() {
+
+ it('multiple participants', inject(integrationTest([ 'Participant_0pgdgt4', 'Participant_1id96b4' ])));
+
+ });
+
+ });
+
});