/*
* 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 treePanel for WorkflowEditorTool
*/
Ext.define('Ametys.plugins.workflow.trees.WorkflowEditorTree', {
extend: 'Ext.tree.Panel',
statics: {
/**
* Workflow label renderer
* @param {Object} value The data value
* @param {Object} metaData A collection of metadata about the current cell
* @param {Ext.data.Model} record The record
* @return {String} The html representation of the targets
*/
renderLabels: function(value, metaData, record)
{
var data = record.getData();
var label = data.label;
if (!record.isRoot())
{
label += ' ('+ data.elementId + ')';
}
return label;
},
/**
* Workflow target renderer
* @param {Object} value The data value
* @param {Object} metaData A collection of metadata about the current cell
* @param {Ext.data.Model} record The record
* @return {String} The html representation of the targets
*/
renderTargets: function(value, metaData, record)
{
var data = record.getData();
var html = "";
for (var key in data.targetedStepNames)
{
html += '<img src="' + data.targetedStepIcons[key] + '" class="a-grid-icon a-grid-icon-workflow"/> ' + data.targetedStepNames[key] + ' ('+ key + ')<br/>';
};
return html;
}
},
constructor: function(config)
{
config.store = this._createStore(config);
var plugins = config.plugins;
config.plugins = Ext.isArray(plugins) ? plugins : (plugins == null ? [] : [plugins]);
if (config.allowEdition)
{
config.plugins.push({
ptype: 'cellediting',
clicksToEdit: 1,
editAfterSelect: true,
listeners: {
'beforeedit': this._onBeforeEdit,
'edit': this._onEdit,
scope: this
}
});
}
this.callParent(arguments);
},
/**
* Init root node
* @param {String} label title of the workflow
*/
initRootNodeParameter: function (workflowName, label, hasChanges)
{
this._workflowName = workflowName;
this.setRootNode({
type: 'root',
text: label,
label: label,
hasChanges: hasChanges,
iconCls: 'a-tree-glyph ametysicon-workflow',
expanded: false
});
this.getSelectionModel().select(0);
},
/**
* Creates the workflow editor tree store
* @param {Object} config The configuration
* @return {Ext.data.Store} The workflow store
* @private
*/
_createStore: function(config)
{
var store = Ext.create('Ext.data.TreeStore', {
model: 'Ametys.plugins.workflow.models.WorkflowTreeModel',
sorters: [
{property: 'text', direction:'ASC'}
],
proxy: {
type: 'ametys',
role: 'org.ametys.plugins.workflow.dao.WorkflowStepDAO',
methodName: 'getStepNodes',
methodArguments: ["nodeId", "workflowName"],
reader: {
type: 'json',
rootProperty: 'steps'
}
},
listeners: {
'beforeload': {fn: this._onBeforeLoad, scope: this},
'load': {fn: this._initCache, scope: this}
},
autoLoad: false,
root: {
id: 'root',
expanded: false
}
});
return store;
},
/**
* Function called before loading the 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
*/
_onBeforeLoad: function(store, operation)
{
operation.setParams(Ext.apply(operation.getParams() || {}, {
nodeId: operation.node.getId(),
workflowName: this._workflowName
}));
},
/**
* @private
* Initialize the workflow cache
* @param {Ext.tree.Panel} tree the current tree
* @param {Object} records the content of the tree store
*/
_initCache: function(tree, records)
{
var steps = {};
var transitions = {};
for (var i in records)
{
var record = records[i].data;
if (record.elementType == "step")
{
steps[record.elementId] = record.label;
}
else if (record.elementType == "action")
{
transitions[record.elementId] = record.label;
}
}
},
/**
* @private
* Listener called before cell editing
* @param {Ext.grid.plugin.CellEditing} editor The cell editor
* @param {Object} context An editing context event with the following properties:
* @param {Ext.data.Model} context.record The record being edited.
*/
_onBeforeEdit: function(editor, context)
{
var record = context.record;
return record && !record.isRoot() && record.getData().id != "step0" && record.canWrite;
},
/**
* @private
* Listener called after cell editing
* @param {Ext.grid.plugin.CellEditing} editor The cell editor
* @param {Object} context An editing context with the following properties:
* @param {Ext.data.Model} context.record The record being edited.
* @param {Mixed} context.value The field's current value.
* @param {Mixed} context.originalValue The original value before being edited.
*/
_onEdit: function(editor, context)
{
var record = context.record,
newName = context.value,
oldName = context.originalValue;
if (newName != oldName)
{
var elementId = parseInt(record.getData().elementId);
var elementType = record.getData().elementType;
if (elementType == "step")
{
Ametys.plugins.workflow.dao.WorkflowStepDAO.editStepLabel(
[this._workflowName, parseInt(elementId), newName],
Ext.bind(renameCb, this)
);
}
else
{
var stepId = record.parentNode.getData().elementId;
Ametys.plugins.workflow.dao.WorkflowTransitionDAO.editTransitionLabel(
[this._workflowName, parseInt(stepId), parseInt(elementId), newName],
Ext.bind(renameCb, this)
);
}
function renameCb(response)
{
if (!response || !response.workflowName)
{
// edit failed
record.beginEdit();
record.set('text', oldName);
record.endEdit();
record.commit();
}
}
}
},
/**
* Listener on WorkflowElementSelected
* @param {Ametys.message.MessageTarget} target The message target
* @param {Function} cb The callback
*/
onWorkflowElementSelected: function(nodeId)
{
var tree = this,
store = tree.getStore(),
selModel = tree.getSelectionModel(),
selection = selModel.getSelection(),
record = store.getById(nodeId);
if (record)
{
if (selection.length > 0 && record == selection[0])
{
// same selection
return;
}
selModel.select(record);
this.getView().focusNode(record);
//tree is expanded at initiation record should never be null
}
},
/**
* Listener on workflow element created or modified, reload the tree and select new element on edition mode
* @param {String} editedId id of the edited node
* @param {String} parentNodeId id of the edited node's parent
* @param {Boolean} cellEditing true to open edited node in cell edition
*/
onWorkflowElementModified: function(editedId, parentNodeId, cellEditing)
{
var tree = this;
var selModel = tree.getSelectionModel();
var store = tree.getStore();
var parentNode = store.getById(parentNodeId);
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, parentNode) {
if (successful)
{
var node = store.getById(editedId);
selModel.select(node);
if (cellEditing && tree.editingPlugin)
{
tree.editingPlugin.startEdit(node, 0);
}
}
}
})
}
}
});