/*
* Copyright 2023 Anyware Services
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Class defining the preview tool for workflows
*/
Ext.define('Ametys.plugins.workflow.tools.WorkflowPreviewTool', {
extend: "Ametys.tool.SelectionTool",
/**
* @private
* @property {String} _workflowName The id of the workflow displayed.
*/
/**
* @private
* @property {String} _stepId The id of the workflow's step displayed.
*/
/**
* @private
* @property {String} _actionId The id of the workflow's action displayed.
*/
/**
* @private
* @property {String} _elementType The type workflow element displayed.
*/
statics:
{
/**
* Set svg width and height so it fill its space
* @private
*/
_onLoadSVG : function(svgDiv, workflowName)
{
svgDiv.contentDocument.getElementsByTagName("svg")[0].style.width = "100%";
svgDiv.contentDocument.getElementsByTagName("svg")[0].style.height = "100%";
svgPanZoom(document.getElementById('workflowSVG-' + workflowName));
svgDiv.style.display = 'flex';
svgDiv.style.opacity=1; //avoid blink on load
}
},
displayWorkflowDetails: true,
constructor: function(config)
{
this.callParent(arguments);
Ametys.message.MessageBus.on(Ametys.message.Message.CREATED, this._handleMessages, this);
Ametys.message.MessageBus.on(Ametys.message.Message.MODIFIED, this._handleMessages, this);
Ametys.message.MessageBus.on(Ametys.message.Message.DELETED, this._handleMessages, this);
Ametys.message.MessageBus.on(Ametys.plugins.workflow.tools.WorkflowEditorTool.WORKFLOW_SAVED, this.showOutOfDate, this);
this._isGraphvizSupported = config["isGraphvizSupported"] == true || config["isGraphvizSupported"] == 'true';
},
getMBSelectionInteraction: function()
{
return Ametys.tool.Tool.MB_TYPE_ACTIVE;
},
setParams: function (params)
{
this._workflowName = params.workflowId;
this._forcedElementType = params.elementType;
this._elementType = this._forcedElementType;
this._stepId = params.stepId;
this._actionId = params.actionId;
this.callParent(arguments);
this.showOutOfDate();
},
refresh: function()
{
this._isInitialized = true;
if (this._forcedElementType)
{
this._elementType = this._forcedElementType;
this._forcedElementType = null;
}
else
{
var targets = this.getCurrentSelectionTargets();
if (targets.length == 1)
{
this._setElementTypeAndId(targets[0]);
}
}
if (this._elementType == "workflow")
{
this.getContentPanel().getLayout().setActiveItem(0);
this._displayWorkflowDiagram();
}
else if (this._elementType == "step")
{
this.getContentPanel().getLayout().setActiveItem(1);
this._stepPropertyGrid.initGridParameters(this._workflowName, this._stepId);
this._stepPropertyGrid.getStore().load();
this._stepActionGrid.getStore().load();
this._displayStepDiagram();
}
else
{
var me = this;
me.getContentPanel().getLayout().setActiveItem(2);
me._transitionPropertyGrid.initGridParameters(me._workflowName, me._stepId, me._actionId);
me._transitionPropertyGrid.getStore().load();
me._transitionActionGrid.getStore().load();
me._displayActionDiagram();
me._resultConditionTree.initRootNodeParameter(me._workflowName, me._actionId, function() {
this.getSelectionModel().select(0);
this.expandAll();
});
me._conditionTree.initRootNodeParameter(me._workflowName, me._actionId, function() {
this.getSelectionModel().select(0);
this.expandAll();
});
}
this.showUpToDate();
},
sendCurrentSelection: function ()
{
var realTarget = this._getCurrentSelection();
Ext.create('Ametys.message.Message', {
type: Ametys.message.Message.SELECTION_CHANGED,
targets: realTarget,
parameters: {}
});
},
/**
* @private
* Get current selection targets
* @returns {Ametys.message.MessageTarget} the targets
*/
_getCurrentSelection: function()
{
var realTarget = this._getMessageTargetConfiguration("workflow") || [];
if (this._elementType == "step")
{
var stepTarget = this._getMessageTargetConfiguration('step');
stepTarget.subtargets = [];
var propertyTarget= this._getPropertySelection(stepTarget);
if (propertyTarget)
{
stepTarget.subtargets.push(propertyTarget);
}
var functionTarget = this._getFunctionSelection(stepTarget);
if (functionTarget)
{
stepTarget.subtargets.push(functionTarget);
}
realTarget.subtargets = stepTarget;
}
else if (this._elementType == "action")
{
var actionTarget = this._getMessageTargetConfiguration('action');
actionTarget.subtargets = [];
var propertyTarget= this._getPropertySelection(actionTarget);
if (propertyTarget)
{
actionTarget.subtargets.push(propertyTarget);
}
var functionTarget = this._getFunctionSelection(actionTarget);
if (functionTarget)
{
actionTarget.subtargets.push(functionTarget);
}
var conditionTarget = this._getConditionSelection(actionTarget);
if (conditionTarget)
{
actionTarget.subtargets.push(conditionTarget);
}
var resultTarget = this._getResultSelection(actionTarget);
if (resultTarget)
{
actionTarget.subtargets.push(resultTarget);
}
var steptarget = this._getMessageTargetConfiguration('step');
steptarget.subtargets = actionTarget
realTarget.subtargets = steptarget;
}
return realTarget;
},
/**
* @private
* Get the target configuration object for given workflow element type
* @param {String} type The type of workflow element displayed in the preview
* @return {Object} The configuration to create a Ametys.message.MessageTarget. Can be null, if the record is null or not relevant to be a messagetarget.
*/
_getMessageTargetConfiguration: function (type)
{
if (type == 'workflow')
{
return {
id: Ametys.message.MessageTarget.WORKFLOW_OBJECT,
parameters: {
id: this._workflowName
}
};
}
else if (type == 'step')
{
return {
id: Ametys.message.MessageTarget.WORKFLOW_STEP,
parameters: {
id: this._stepId,
label: this._stepLabel,
workflowId: this._workflowName
},
};
}
else if (type == 'action')
{
return {
id: Ametys.message.MessageTarget.WORKFLOW_ACTION,
parameters: {
id: this._actionId,
label: this._actionLabel,
stepId: this._stepId,
workflowId: this._workflowName
}
};
}
return null;
},
/**
* @private
* Get selected property in propertyGrid as target
* @param {Object} currentTarget the workflow element being displayed in the tool
* @returns {Object} the property target
*/
_getPropertySelection: function(currentTarget)
{
var targetIsStep = currentTarget.id == Ametys.message.MessageTarget.WORKFLOW_STEP;
var grid = targetIsStep ? this._stepPropertyGrid : this._transitionPropertyGrid;
var selection = grid.getSelectionModel().getSelection();
if(selection.length > 0)
{
var currentRecord = selection[0].getData();
return {
id: Ametys.message.MessageTarget.WORKFLOW_PROPERTY,
parameters: {
name: currentRecord.id,
value: currentRecord.value,
stepId: targetIsStep ? currentTarget.parameters.id : currentTarget.parameters.stepId,
actionId: targetIsStep ? null : currentTarget.parameters.id,
workflowId: this._workflowName
}
};
}
return null;
},
/**
* @private
* Get selected function in actionGrid as target
* @param {Object} currentTarget the workflow element being displayed in the tool
* @returns {Object} the function target
*/
_getFunctionSelection: function(currentTarget)
{
var targetIsStep = currentTarget.id == Ametys.message.MessageTarget.WORKFLOW_STEP;
var grid = targetIsStep ? this._stepActionGrid : this._transitionActionGrid;
var selection = grid.getSelectionModel().getSelection();
if(selection.length > 0)
{
var currentRecord = selection[0].getData();
return {
id: Ametys.message.MessageTarget.WORKFLOW_FUNCTION,
parameters: {
index: currentRecord.index,
id: (currentRecord.id.split('-'))[1],
type: currentRecord.type,
isLast: currentRecord.isLast,
stepId: targetIsStep ? currentTarget.parameters.id : currentTarget.parameters.stepId,
actionId: targetIsStep ? null : currentTarget.parameters.id,
workflowId: this._workflowName
}
};
}
return null;
},
/**
* @private
* Get selected condition or operator in conditionTree as target
* @param {Object} currentTarget the workflow element being displayed in the tool
* @returns {Object} the condition target
*/
_getConditionSelection: function(currentTarget)
{
var selection = this._conditionTree.getSelectionModel().getSelection();
if(selection.length > 0)
{
var currentRecord = selection[0].getData();
return this._getConditionTarget(currentTarget, currentRecord);
}
return null;
},
/**
* @private
* Get selected step result or child condition in result tree
* @param {Object} currentTarget the workflow element being displayed in the tool
* @returns {Object} a target
*/
_getResultSelection: function(currentTarget)
{
var selection = this._resultConditionTree.getSelectionModel().getSelection();
if(selection.length > 0)
{
var currentRecord = selection[0].getData();
var nodeId = currentRecord.id,
path = nodeId.split('-')
if (path.length == 1 || path.length == 2 && path[1] == 1)
{
return {
id: Ametys.message.MessageTarget.WORKFLOW_RESULT,
parameters: {
nodeId: nodeId,
nodeLabel: currentRecord.label,
isConditional: currentRecord.isConditional,
actionId: currentTarget.parameters.id,
stepId: currentTarget.parameters.stepId,
workflowId: this._workflowName
}
};
}
else
{
return this._getConditionTarget(currentTarget, currentRecord);
}
}
return null;
},
/**
* @private
* Get selected condition target
* @param {Object} currentTarget the workflow element being displayed in the tool
* @param {Object} currentRecord the data stocked in the selected row in the tree
* @returns {Object} a condition(s) target
*/
_getConditionTarget: function(currentTarget, currentRecord)
{
var nodeId = currentRecord.id;
var id = nodeId.includes("condition") ? Ametys.message.MessageTarget.WORKFLOW_CONDITION : Ametys.message.MessageTarget.WORKFLOW_CONDITIONS_OPERATOR;
if (id == Ametys.message.MessageTarget.WORKFLOW_CONDITIONS_OPERATOR)
{
var type = nodeId.endsWith("and", nodeId.length-1)
? "AND"
: "OR";
return {
id: id,
parameters: {
nodeId: nodeId,
type: type,
actionId: currentTarget.parameters.id,
stepId: currentTarget.parameters.stepId,
workflowId: this._workflowName
}
};
}
else
{
return {
id: id,
parameters: {
nodeId: nodeId,
conditionId: currentRecord.conditionId,
actionId: currentTarget.parameters.id,
stepId: currentTarget.parameters.stepId,
workflowId: this._workflowName
}
};
}
},
areSameTargets: function (target1, target2)
{
var targetGrids = target2.getSubtarget(function(target) {
return (
target.getId() == Ametys.message.MessageTarget.WORKFLOW_CONDITION
|| target.getId() == Ametys.message.MessageTarget.WORKFLOW_CONDITIONS_OPERATOR
|| target.getId() == Ametys.message.MessageTarget.WORKFLOW_PROPERTY
|| target.getId() == Ametys.message.MessageTarget.WORKFLOW_FUNCTION
|| target.getId() == Ametys.message.MessageTarget.WORKFLOW_RESULT
);
}, 0);
if (targetGrids)
{
//no action or step has been modified we only need to reload the grid store
return true;
}
// There is only one target in selection at a time, so this function is called when sending selection changed message
// Because the is more object properties being displayed than there is in the bus message, we can't check every changes made on the target
// so we assume there is a change when this function is called and a refresh is needed
return false;
},
/**
* On selection changed, update id and type of current element to change display
* @param {Ametys.message.MessageTarget} workflowTarget the current selection target
* @private
*/
_setElementTypeAndId: function(workflowTarget)
{
this._workflowName = workflowTarget.getParameters().id;
var stepTargets = workflowTarget.getSubtargets(function(target){return target.getId() == Ametys.message.MessageTarget.WORKFLOW_STEP});
if (stepTargets.length == 1)
{
this._stepId = stepTargets[0].getParameters().id;
this._stepLabel = stepTargets[0].getParameters().label;
var actionTargets = stepTargets[0].getSubtargets(function(target){return target.getId() == Ametys.message.MessageTarget.WORKFLOW_ACTION});
if (actionTargets.length == 1)
{
this._elementType = "action";
this._actionId = actionTargets[0].getParameters().id;
this._actionLabel = actionTargets[0].getParameters().label;
}
else
{
this._elementType = "step";
}
}
else
{
this._elementType = "workflow";
}
},
/**
* Display the state diagram for this workflow
* @private
*/
_displayWorkflowDiagram: function()
{
if (!this._isGraphvizSupported)
{
var me = this;
window.setTimeout(function() { me._diagram.mask("{{i18n plugin.workflow:UITOOL_WORKFLOW_EDITOR_UNSUPPORTED_GRAPHVIZ}}", 'ametys-mask-unloading'); }, 0);
}
else
{
var actionArgs = {
"workflowName": this._workflowName,
"context.parameters": encodeURIComponent(Ext.JSON.encode(Ametys.getAppParameters()))
};
var html = `<object onload='Ametys.plugins.workflow.tools.WorkflowPreviewTool._onLoadSVG(this, "${this._workflowName}")' `
+ `id='workflowSVG-${this._workflowName}' class='workflowSVG' `
+ "style='opacity: 0;"
+ "width: 100%;"
+ "height: 100%;' "
+ "type='image/svg+xml' "
+ `data='${this._buildDiagramURL("workflow", actionArgs)}'/>`;
this._diagram.setHtml(html);
}
},
/**
* Display the step diagram
* @private
*/
_displayStepDiagram: function()
{
var actionArgs = {
"workflowName": this._workflowName,
"stepId": this._stepId,
"context.parameters": encodeURIComponent(Ext.JSON.encode(Ametys.getAppParameters()))
};
this._displayDiagram(this._stepDiagram, "step", actionArgs);
},
/**
* Display the transition diagram
* @private
*/
_displayActionDiagram: function()
{
var actionArgs = {
"workflowName": this._workflowName,
"actionId": this._actionId,
"context.parameters": encodeURIComponent(Ext.JSON.encode(Ametys.getAppParameters()))
};
this._displayDiagram(this._actionDiagram, "transition", actionArgs);
},
/**
* @private
*/
_displayDiagram: function(diagram, action, actionArgs)
{
diagram.setHtml(
"<div style='opacity: 0;"
+ "min-width: 100%;"
+ "width: fit-content;"
+ "height: fit-content;"
+ "min-height: 100%;"
+ "justify-content: center;"
+ "align-items: center;'>"
+ `<object onload='this.parentNode.style.display="flex";` // hack to get svg centered and on full size
+ "this.parentNode.style.opacity=1'" // avoid blink on load
+ "type='image/svg+xml'"
+ `data='${this._buildDiagramURL(action, actionArgs)}'/>`
+ "</div>"
);
},
/**
* @private
*/
_buildDiagramURL: function(action, actionArgs)
{
var urlArgs = "";
for (var argName in actionArgs)
{
urlArgs += argName + "=" + actionArgs[argName] + "&";
}
return `${Ametys.CONTEXT_PATH}/plugins/workflow/${action}-graph.svg?${urlArgs}`;
},
createPanel: function()
{
return new Ext.container.Container ({
layout: {
type: 'card',
deferredRender: false,
activeItem: 0
},
cls: 'uitool-workflow',
items: [
this._createWorkflowPanel(),
this._createStepPanel(),
this._createActionPanel()
]
});
},
/**
* Create the panel for displaying the whole workflow diagram
* @return {Object} the panel
* @private
*/
_createWorkflowPanel: function()
{
this._diagram = Ext.create("Ext.Component", {
flex: 1,
scrollable: false
});
return this._diagram;
},
/**
* Create the panel for displaying a workflow step diagram and grid showing its properties
* @return {Object} the panel
* @private
*/
_createStepPanel: function()
{
this._stepDiagram = Ext.create("Ext.Component", {
flex: 1
});
this._stepPropertyGrid = this._createPropertyGrid("step");
this._stepActionGrid = this._createActionGrid("step");
var gridsPanel = Ext.create('Ext.Container', {
flex: 0.1,
layout: {
type: 'hbox',
align: 'stretch'
},
minHeight: 250,
border: false,
items: [
this._stepActionGrid,
{xtype: 'splitter'},
this._stepPropertyGrid
],
});
var panel = Ext.create('Ext.Container', {
cls: 'uitool-workflow',
layout: {
type: 'vbox',
align: 'stretch'
},
border: false,
items: [
this._stepDiagram,
{xtype: 'splitter'},
gridsPanel
]
});
return panel;
},
/**
* Create the panel for displaying a workflow action diagram and grids or trees showing its properties
* @return {Object} an Ext.Container as the action panel
* @private
*/
_createActionPanel: function()
{
this._actionDiagram = Ext.create("Ext.Component", {
flex: 1
});
this._transitionActionGrid = this._createActionGrid("action");
this._transitionPropertyGrid = this._createPropertyGrid("action");
this._resultConditionTree = this._createResultTree();
this._conditionTree = this._createConditionTree();
var gridsPanel = Ext.create('Ext.Container', {
flex: 0.1,
layout: {
type: 'hbox',
align: 'stretch'
},
minHeight: 280,
border: false,
items: [
this._conditionTree,
{xtype: 'splitter'},
this._transitionActionGrid,
{xtype: 'splitter'},
this._resultConditionTree,
{xtype: 'splitter'},
this._transitionPropertyGrid
],
});
var panel = Ext.create('Ext.Container', {
cls: 'uitool-workflow',
layout: {
type: 'vbox',
align: 'stretch'
},
border: false,
items: [
this._actionDiagram,
{xtype: 'splitter'},
gridsPanel
]
});
return panel;
},
/**
* Create the action grid panel
* @param {String} elementType the element type can be 'step' or 'action'
* @returns {Object} a Ext.grid.Panel as this action grid
* @private
*/
_createActionGrid : function(elementType)
{
var actionGrid = Ext.create('Ext.grid.Panel', {
title: "{{i18n plugin.workflow:PLUGINS_WORKFLOW_COLUMN_FUNCTIONS_LABEL}}",
collapsible: true,
titleCollapse: true,
collapseDirection: 'left',
flex: 0.4,
minWidth: 150,
store: (elementType == "step") ? this._getStepActionStore() : this._getTransitionActionStore(),
columns: [
{
dataIndex: "index",
hidden: true,
sortable: false
},
{
dataIndex: "displayValue",
flex: 0.8,
sortable: false
},
{
dataIndex: 'typeLabel',
flex: 0.2,
hidden: true,
sortable: false
}
],
features: [
{
ftype: 'grouping',
enableGroupingMenu: false,
groupHeaderTpl: [
'{name:this.formatState}',
{
formatState: function(group) {
switch (group) {
case "1":
return elementType == "step" ? "{{i18n PLUGINS_WORKFLOW_ADD_FUNCTION_DIALOG_STEP_PREFUNCTION_TYPE}}" : "{{i18n PLUGINS_WORKFLOW_ADD_FUNCTION_DIALOG_PREFUNCTION_TYPE}}";
case "2":
default:
return elementType == "step" ? "{{i18n PLUGINS_WORKFLOW_ADD_FUNCTION_DIALOG_STEP_POSTFUNCTION_TYPE}}" : "{{i18n PLUGINS_WORKFLOW_ADD_FUNCTION_DIALOG_POSTFUNCTION_TYPE}}";
}
}
}
]
}
],
listeners: {
'cellclick': {fn: this._onFunctionSelected, scope: this},
'selectionchange': {fn: this.sendCurrentSelection, scope: this}
}
});
return actionGrid;
},
/**
* @private
* Unselect every grids other than the functions one
*/
_onFunctionSelected: function()
{
if (this._elementType == 'action')
{
this._resultConditionTree.getSelectionModel().deselectAll();
this._conditionTree.getSelectionModel().deselectAll();
this._transitionPropertyGrid.getSelectionModel().deselectAll();
}
else
{
this._stepPropertyGrid.getSelectionModel().deselectAll();
}
},
/**
* Create the action grid panel's store
* @returns {Object} a Ext.data.Store as the store of this _stepActionGrid
* @private
*/
_getStepActionStore: function()
{
var store = Ext.create('Ext.data.Store', {
model: Ametys.plugins.workflow.models.FunctionsGridModel,
proxy: {
type: 'ametys',
role: 'org.ametys.plugins.workflow.dao.WorkflowFunctionDAO',
methodName: 'getStepFunctions',
methodArguments: ["workflowName", "stepId"],
reader: {
type: 'json',
rootProperty: 'data'
}
},
groupField: 'typeOrder',
listeners: {
'beforeload': {fn: this._onStepBeforeLoad, scope: this},
'load': {fn: this._onActionGridLoad , scope: this }
}
});
return store;
},
/**
* Create the action grid panel's store
* @returns {Object} a Ext.data.Store as the store of this _transitionActionGrid
* @private
*/
_getTransitionActionStore: function()
{
var store = Ext.create('Ext.data.Store', {
model: 'Ametys.plugins.workflow.models.FunctionsGridModel',
proxy: {
type: 'ametys',
role: 'org.ametys.plugins.workflow.dao.WorkflowFunctionDAO',
methodName: 'getActionFunctions',
methodArguments: ["workflowName", "actionId"],
reader: {
type: 'json',
rootProperty: 'data'
}
},
groupField: 'typeOrder',
listeners: {
'beforeload': {fn: this._onActionBeforeLoad, scope: this},
'load': {fn: this._onActionGridLoad , scope: this }
}
})
return store;
},
/**
* Listener on action grid store load, display number of rows in the panel's title
* @param {Object} store the store being loaded
* @param {record} record the store content
* @private
*/
_onActionGridLoad: function(store, record)
{
var grid = (this._elementType == 'action')
? this._transitionActionGrid
: this._stepActionGrid;
grid.expand();
grid.setTitle(grid.getInitialConfig('title') + " ("+ record.length + ")");
},
/**
* Create the property grid
* @param {String} elementType the element type can be 'step' or 'action'
* @returns {Object} the property grid
* @private
*/
_createPropertyGrid: function(elementType)
{
var grid = Ext.create('Ametys.plugins.workflow.grids.PropertyGridPanel', {
title: "{{i18n plugin.workflow:PLUGINS_WORKFLOW_COLUMN_PROPERTIES_LABEL}}",
collapsible: true,
titleCollapse: true,
collapseDirection: 'left',
allowEdition: true,
flex: 0.4,
minWidth: 150,
elementType: elementType,
columns: [
{
dataIndex: "id",
editor: {
xtype: 'textfield',
allowBlank: false,
selectOnFocus: true
},
flex: 1,
sortable: false
},
{
dataIndex: "value",
editor: {
xtype: 'textfield',
selectOnFocus: true
},
flex: 1,
sortable: false
}
],
listeners: {
'cellclick': {fn: this._onPropertySelected, scope: this},
'selectionchange': {fn: this.sendCurrentSelection, scope: this}
}
});
return grid;
},
/**
* @private
* Unselect every grids other than the property's one
*/
_onPropertySelected: function()
{
if (this._elementType == 'action')
{
this._resultConditionTree.getSelectionModel().deselectAll();
this._conditionTree.getSelectionModel().deselectAll();
this._transitionActionGrid.getSelectionModel().deselectAll();
}
else
{
this._stepActionGrid.getSelectionModel().deselectAll();
}
},
/**
* Create the condition tree
* @returns {Object} a ConditionTreePanel
* @private
*/
_createConditionTree: function()
{
this._conditionTree = Ext.create('Ametys.plugins.workflow.trees.ConditionTreePanel', {
title: "{{i18n plugin.workflow:PLUGINS_WORKFLOW_COLUMN_CONDITION_LABEL}}",
titleCollapse: true,
rootVisible: false,
allowDeselect: true,
collapsible: true,
collapseDirection: 'left',
scrollable: true,
flex: 0.4,
minWidth: 150,
cls: 'uitool-conditions-tree',
displayField: 'label',
listeners: {
'cellclick': {fn: this._onConditionSelected, scope: this},
'selectionchange': {fn: this.sendCurrentSelection, scope: this}
}
});
return this._conditionTree;
},
/**
* @private
* Unselect every grids other than the condition's one
*/
_onConditionSelected: function()
{
if (this._elementType == 'action')
this._resultConditionTree.getSelectionModel().deselectAll();
if (this._elementType == 'action')
{
this._transitionActionGrid.getSelectionModel().deselectAll();
this._transitionPropertyGrid.getSelectionModel().deselectAll();
}
else
{
this._stepActionGrid.getSelectionModel().deselectAll();
this._stepPropertyGrid.getSelectionModel().deselectAll();
}
},
/**
* Create the result tree
* @returns {Object} a ResultTreePanel
* @private
*/
_createResultTree: function()
{
var tree = Ext.create('Ametys.plugins.workflow.trees.ResultTreePanel', {
title: "{{i18n plugin.workflow:PLUGINS_WORKFLOW_COLUMN_RESULTS_CONDITIONS_LABEL}}",
rootVisible: false,
titleCollapse: true,
collapsible: true,
collapseDirection: 'left',
scrollable: true,
flex: 0.4,
minWidth: 150,
cls: 'uitool-result-tree',
displayField: 'label',
listeners: {
'cellclick': {fn: this._onResultSelected, scope: this},
'selectionchange': {fn: this.sendCurrentSelection, scope: this}
}
});
return tree;
},
/**
* @private
* Unselect every grids other than the result's one
*/
_onResultSelected: function()
{
this._conditionTree.getSelectionModel().deselectAll();
if (this._elementType == 'action')
{
this._transitionActionGrid.getSelectionModel().deselectAll();
this._transitionPropertyGrid.getSelectionModel().deselectAll();
}
else
{
this._stepActionGrid.getSelectionModel().deselectAll();
this._stepPropertyGrid.getSelectionModel().deselectAll();
}
},
/**
* Function called before loading a transition grid store
* @param {Ext.data.Store} store The store
* @param {Ext.data.operation.Operation} operation The object that will be passed to the Proxy to load the store
* @private
*/
_onActionBeforeLoad: function(store, operation)
{
operation.setParams(Ext.apply(operation.getParams() || {}, {
workflowName: this._workflowName,
actionId: parseInt(this._actionId)
}));
},
/**
* Function called before loading step grids stores
* @param {Ext.data.Store} store The store
* @param {Ext.data.operation.Operation} operation The object that will be passed to the Proxy to load the store
* @private
*/
_onStepBeforeLoad: function(store, operation)
{
operation.setParams(Ext.apply(operation.getParams() || {}, {
workflowName: this._workflowName,
stepId: parseInt(this._stepId)
}));
},
/**
* Listener on created, modified and deleted messages, select the new element
* @param {Ametys.message.Message} message The selection message.
* @private
*/
_handleMessages: function(message)
{
if (!this._isInitialized)
{
return;
}
var targetProperty = message.getTarget(function(target) {
return target.getId() == Ametys.message.MessageTarget.WORKFLOW_PROPERTY
}, 0);
if (targetProperty)
{
this._onPropertySelected();
this._onPropertyModifiedOrDeleted(targetProperty);
return;
}
var targetFunction = message.getTarget(function(target) {
return target.getId() == Ametys.message.MessageTarget.WORKFLOW_FUNCTION
}, 0);
if (targetFunction)
{
this._onFunctionSelected();
this._onFunctionModifiedOrDeleted(targetFunction);
return;
}
var targetResult = message.getTarget(function(target) {
return (target.getId() == Ametys.message.MessageTarget.WORKFLOW_RESULT);
}, 0);
if (targetResult)
{
this._onResultSelected();
this._onConditionModifiedOrDeleted(targetResult);
return;
}
var targetCondition = message.getTarget(function(target) {
return (target.getId() == Ametys.message.MessageTarget.WORKFLOW_CONDITION || target.getId() == Ametys.message.MessageTarget.WORKFLOW_CONDITIONS_OPERATOR);
}, 0);
if (targetCondition)
{
this._onConditionSelected()
this._onConditionModifiedOrDeleted(targetCondition);
return;
}
var actionTarget = message.getTarget(function(target) {
return (target.getId() == Ametys.message.MessageTarget.WORKFLOW_ACTION);
}, 0);
if (actionTarget)
{
this._onActionSelected(actionTarget);
return;
}
var stepTarget = message.getTarget(function(target) {
return (target.getId() == Ametys.message.MessageTarget.WORKFLOW_STEP);
}, 0);
if (stepTarget)
{
this._onStepSelected(stepTarget);
return;
}
var workflowTarget = message.getTarget(function(target) {
return target.getId() == Ametys.message.MessageTarget.WORKFLOW_OBJECT
}, 0);
if (workflowTarget)
{
this._onWorkflowSelected();
return;
}
},
/**
* @private
* Update the tool's action properties
*/
_onActionSelected: function(actionTarget)
{
this._elementType = "action";
this._actionId = actionTarget.getParameters().id;
this._actionLabel = actionTarget.getParameters().label;
this.sendCurrentSelection();
},
/**
* @private
* Update the tool's step properties
*/
_onStepSelected: function(stepTarget)
{
this._actionId = null;
this._actionLabel = null;
this._elementType = "step";
this._stepId = stepTarget.getParameters().id;
this._stepLabel = stepTarget.getParameters().label;
this.sendCurrentSelection();
},
/**
* @private
* Update the tool's workflow properties
*/
_onWorkflowSelected: function()
{
this._actionId = null;
this._actionLabel = null;
this._elementType = "workflow";
this._stepId = null;
this._stepLabel = null;
this.sendCurrentSelection();
},
/**
* @private
* Reload property store
* @param {Ametys.message.MessageTarget} target The property target.
*/
_onPropertyModifiedOrDeleted(target)
{
this.focus();
var grid = target.getParameters().actionId != null ? this._transitionPropertyGrid : this._stepPropertyGrid;
var store = grid.getStore();
store.load({
callback: Ext.bind(function() {
grid.getSelectionModel().select(store.getById(target.getParameters().name));
}, grid)
});
},
/**
* @private
* Reload function store
* @param {Ametys.message.MessageTarget} target The function target.
*/
_onFunctionModifiedOrDeleted(target)
{
this.focus();
var grid = target.getParameters().actionId != null ? this._transitionActionGrid : this._stepActionGrid;
var newNodeId = target.getParameters().type + "-" + target.getParameters().id + "-" + target.getParameters().index;
var store = grid.getStore();
store.load({
callback: Ext.bind(function() {
grid.getSelectionModel().select(store.getById(newNodeId));
}, grid)
});
},
/**
* @private
* Reload store and select edited node
* @param {Ametys.message.MessageTarget} target The condition(s) target.
*/
_onConditionModifiedOrDeleted(target)
{
this.focus();
var isInResultTree = target.getParameters().nodeId.startsWith("step");
var tree = isInResultTree ? this._resultConditionTree : this._conditionTree;
var nodeId = target.getParameters().nodeId;
var indexOf = nodeId.lastIndexOf("-");
var parentId = indexOf != -1 ? nodeId.substring(0, indexOf) : null;
if (parentId != null && isInResultTree)
{
var parentPath = parentId.split('-');
if (parentPath.length == 2 && parentPath[1] == "and0")
{
parentId = parentPath[0];
}
}
var store = tree.getStore();
var parentNode = store.getById(parentId);
store.load({
scope: tree,
callback: function ()
{
this.expandAll(selectNewNode, tree);
}
});
function selectNewNode()
{
if(parentNode == null)
{
parentNode = store.getRootNode();
}
var path = parentNode.getPath();
tree.expandPath(path, {
field: "id",
separator: "/",
callback: function (successful) {
if (successful)
{
tree.getSelectionModel().select(store.getById(nodeId));
}
}
})
}
}
});
/**
* Define the model for pre and post functions
*/
Ext.define('Ametys.plugins.workflow.models.FunctionsGridModel', {
extend: 'Ext.data.Model',
fields: [
{name: 'index', type: 'number'},
{name: 'type', type: 'string'},
{
name: 'typeOrder',
convert: function(value, record)
{
var data = record.data;
return data.type == "PRE" ? 1 : 2;
}
},
{name: 'description', type: 'string'},
{name: 'displayValue',
convert: function(value, record)
{
var data = record.data;
if (data.description)
{
return data.description;
}
return data.id;
}
},
{
name: 'id',
convert: function(value, record)
{
var data = record.data;
return data.type + "-" + data.id + "-" + data.index;
}
}
]
});