/*
* 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.
*/
/**
* Actions on workflows
*/
Ext.define('Ametys.plugins.workflow.actions.WorkflowsListActions', {
singleton: true,
/**
* @private
* @property {String[]} _unavailableNames list of used workflow name
*/
/**
* @private
* @property {Boolean} _idChanged false by default, set to true if user has typed in the id field
*/
/**
* @private
* @property {String} _mode 'new' or 'edit'
*/
/**
* @private
* @property {String} _workflowName id of currently selected workflow
*/
/**
* @private
* @property {Ametys.window.DialogBox} _creationWorkflowBox the creation dialog box for workflows
*/
/**
* @private
* @property {Ametys.window.DialogBox} _duplicateWorkflowBox the duplication dialog box for workflows
*/
/**
* @private
* @property {Ametys.window.DialogBox} _editWorkflowBox the creation dialog box for workflows
*/
/**
* Open the workflowPreviewTool for this workflow element and the WorkflowEditorTool if it's not open
* @param {Ametys.ribbon.element.ui.ButtonController} controller The controller calling this function
*/
showWorkflow : function(controller)
{
var target = Ametys.message.MessageTargetHelper.findTarget(controller.getMatchingTargets(), Ametys.message.MessageTarget.WORKFLOW_OBJECT);
if (!target)
{
return;
}
var targetParameters = target.getParameters();
var workflowName = targetParameters.id;
if (Ametys.tool.ToolsManager.getTool("uitool-workflow-editor$" + workflowName) == null)
{
Ametys.tool.ToolsManager.openTool("uitool-workflow-editor", {id: workflowName});
Ametys.tool.ToolsManager.openTool("uitool-workflow-preview", {workflowId: workflowName, elementType: "workflow"});
}
else
{
var targetStep = target.getSubtarget(function(target){return target.getId() == Ametys.message.MessageTarget.WORKFLOW_STEP});
if (targetStep)
{
var stepId = targetStep.getParameters().id;
var stepLabel = targetStep.getParameters().label;
var targetAction = targetStep.getSubtarget(function(target){return target.getId() == Ametys.message.MessageTarget.WORKFLOW_ACTION});
if (targetAction)
{
var actionId = targetAction.getParameters().id;
var actionLabel = targetAction.getParameters().label;
Ametys.tool.ToolsManager.openTool("uitool-workflow-preview", {workflowId: workflowName, elementType: "action", stepId: stepId, stepLabel: stepLabel, actionId: actionId, actionLabel: actionLabel});
}
else
{
Ametys.tool.ToolsManager.openTool("uitool-workflow-preview", {workflowId: workflowName, elementType: "step", stepId: stepId, stepLabel: stepLabel});
}
}
else
{
Ametys.tool.ToolsManager.openTool("uitool-workflow-preview", {workflowId: workflowName, elementType: "workflow"});
}
}
},
/**
* Create a new workflow
* @param {Ametys.ribbon.element.ui.ButtonController} controller The controller calling this function
*/
createWorkflow: function(controller)
{
this._mode = "new";
Ametys.plugins.workflow.dao.WorkflowsDAO.getWorkflowInfos([null], Ext.bind(this._getWorkflowsNameCB, this));
},
/**
* @private
* Create a dialog box
* @return {Ametys.window.DialogBox} the dialog box
*/
_createDialogBox: function()
{
var dialogBox = Ext.create('Ametys.window.DialogBox', {
title : this._getTitle(),
iconCls: this._getIcon(),
width:400,
layout: 'anchor',
defaults:
{
msgTarget: "side",
cls: 'ametys',
padding: 5,
anchor: '100%',
labelAlign: "top",
labelSeparator: "",
labelStyle: "font-weight:bold",
},
items : this._getItems(),
defaultFocus: 'label',
selectDefaultFocus: true,
closeAction: 'hide',
referenceHolder: true,
defaultButton: 'validate',
buttons : [
{
reference: 'validate',
text: "{{i18n PLUGINS_WORKFLOW_CREATE_DIALOG_OK_BTN}}",
handler: Ext.bind(this._ok, this)
},
{
text: "{{i18n PLUGINS_WORKFLOW_CREATE_DIALOG_CANCEL_BTN}}",
handler: Ext.bind(this._hideDialogBox, this)
}
]
});
if (this._mode == "new")
{
var labelField = dialogBox.getComponent('label');
var idField = dialogBox.getComponent('id');
labelField.addListener("change", function(field, value)
{
if (!this._idChanged && value && Object.values(value).length > 0)
{
var label = Object.values(value)[0];
var id = (Ext.String.deemphasize(label)).replace(/[^a-z0-9]/gi, "-");
id = id.toLowerCase();
idField.setValue(id);
}
}, this);
idField.addListener("keyup", function(field, value)
{
this._idChanged = true;
}, this);
}
return dialogBox;
},
/**
* @private
* Get the dialog box depending on the mode
* @return {Ametys.window.DialogBox} the dialog box
*/
_getDialogBox: function()
{
var box="";
switch(this._mode)
{
case 'edit':
box = this._editWorkflowBox;
break;
case 'new':
box = this._creationWorkflowBox;
break;
case 'duplicate':
default:
box = this._duplicateWorkflowBox;
break;
}
return box;
},
/**
* @private
* Get the dialog box title depending on the mode
* @returns {String} the title
*/
_getTitle: function()
{
var title="";
switch(this._mode)
{
case 'edit':
title = "{{i18n PLUGINS_WORKFLOW_RENAME_WORKFLOW_DIALOG_TITLE}}";
break;
case 'new':
title = "{{i18n PLUGINS_WORKFLOW_CREATE_WORKFLOW_DIALOG_TITLE}}";
break;
case 'duplicate':
default:
title = "{{i18n PLUGINS_WORKFLOW_DUPLICATE_WORKFLOW_DIALOG_TITLE}}";
break;
}
return title;
},
/**
* @private
* Get the dialog box title depending on the mode
* @returns {String} the icon name
*/
_getIcon: function()
{
var icon="";
switch(this._mode)
{
case 'edit':
icon = "ametysicon-text1";
break;
case 'new':
icon = "ametysicon-add64";
break;
case 'duplicate':
default:
icon = "ametysicon-file229"
break;
}
return icon;
},
/**
* @private
* Get common items between new and edit mode
* @returns {Ext.form.field[]} the box items
*/
_getItems: function()
{
var items = [
{
xtype: "multilingualstring",
name: 'label',
itemId: 'label',
allowBlank: false,
fieldLabel: "{{i18n PLUGINS_WORKFLOW_CREATE_WORKFLOW_DIALOG_LABEL}} *",
ametysDescription: "{{i18n PLUGINS_WORKFLOW_CREATE_WORKFLOW_DIALOG_LABEL_DESC}}"
}
];
if (this._mode == "new" || this._mode == "duplicate")
{
items = items.concat(
{
xtype: "textfield",
name: 'id',
itemId: 'id',
allowBlank: false,
fieldLabel: "{{i18n PLUGINS_WORKFLOW_CREATE_WORKFLOW_DIALOG_ID}} *",
ametysDescription: "{{i18n PLUGINS_WORKFLOW_CREATE_WORKFLOW_DIALOG_ID_DESC}}",
enableKeyEvents: true,
regex: /^[a-zA-Z0-9\-]*$/,
regexText: "{{i18n PLUGINS_WORKFLOW_INVALID_CHARACTER_WORKFLOW_NAME_ERROR}}",
validator: Ext.bind(this._idValidator, this)
}
);
}
return items;
},
/**
* @private
* Validator for id field
* @param {String} id the id value to check
* @returns {Object} true if pass validation, error msg if not
*/
_idValidator: function(id)
{
return this._mode == "new" && this._unavailableNames.includes(id)
? "{{i18n PLUGINS_WORKFLOW_CREATE_WORKFLOW_DIALOG_ID_ERROR}}"
: true;
},
/**
* @private
* Callback of getWorkflowsName set form values
* @param {Object} response the server response
* @param {Object} response.workflowNames list of already used workflow names
*/
_getWorkflowsNameCB: function(response)
{
if (!this._creationWorkflowBox)
{
this._creationWorkflowBox = this._createDialogBox();
}
this._idChanged = false;
this._unavailableNames = response.workflowNames;
this._creationWorkflowBox.getComponent('id').reset();
this._creationWorkflowBox.getComponent('label').reset();
this._creationWorkflowBox.show();
},
/**
* @private
* Create or edit transition with field values
*/
_ok: function ()
{
var box = this._getDialogBox();
if (this._hasInvalid(box))
{
return;
}
var labels = box.getComponent('label').getValue();
if (this._mode == "edit")
{
Ametys.plugins.workflow.dao.WorkflowsDAO.renameWorkflow([this._workflowName, labels], Ext.bind(this._hideDialogBox, this));
}
else
{
this._workflowName = box.getComponent('id').getValue();
if (this._mode == "new")
{
Ametys.plugins.workflow.dao.WorkflowsDAO.createWorkflow([labels, this._workflowName], Ext.bind(this._hideDialogBox, this));
}
else
{
Ametys.plugins.workflow.dao.WorkflowsDAO.duplicateWorkflow([this._workflowName, labels, this._duplicateId], Ext.bind(this._hideDialogBox, this));
}
}
},
/**
* @private
* Return true if a field is invalid
* {Ametys.window.DialogBox} the dialog box
*/
_hasInvalid: function(box)
{
var fields = box.query("[isFormField]");
var hasInvalid = false;
for (let field of fields)
{
if (!field.isValid())
{
hasInvalid = true;
}
}
return hasInvalid;
},
/**
* Hide the dialog box
* @param {Object} response the server response
* @param {String} response.message an error message if defined
*/
_hideDialogBox: function (response)
{
if (!response || !response.message)
{
var dialogBox = this._getDialogBox();
dialogBox.hide();
}
},
/**
* Rename the workflow
* @param {Ametys.ribbon.element.ui.ButtonController} controller The controller calling this function
*/
renameWorkflow: function(controller)
{
var target = Ametys.message.MessageTargetHelper.findTarget(controller.getMatchingTargets(), Ametys.message.MessageTarget.WORKFLOW_OBJECT);
if (!target)
{
return;
}
var targetParameters = target.getParameters();
this._workflowName = targetParameters.id;
this._mode = "edit";
Ametys.plugins.workflow.dao.WorkflowsDAO.getWorkflowInfos([this._workflowName], Ext.bind(this._getWorkflowLabelsCB, this));
},
/**
* Edit an element of workflow
* @param {Ametys.plugins.workflow.controllers.EditOrDeleteElementController} controller The controller calling this function
*/
editElement: function(controller)
{
var stepTarget = Ametys.message.MessageTargetHelper.findTarget(controller.getMatchingTargets(), Ametys.message.MessageTarget.WORKFLOW_STEP);
var functionTarget = stepTarget.getSubtarget(function(target){return target.getId() == Ametys.message.MessageTarget.WORKFLOW_FUNCTION});
if (functionTarget)
{
Ametys.plugins.workflow.actions.WorkflowFunctionAction.editFunction(functionTarget);
}
else
{
var conditionTarget = stepTarget.getSubtarget(function(target){return target.getId() == Ametys.message.MessageTarget.WORKFLOW_CONDITION});
if (conditionTarget)
{
Ametys.plugins.workflow.actions.WorkflowConditionAction.editCondition(conditionTarget);
}
else
{
var resultTarget = stepTarget.getSubtarget(function(target){return target.getId() == Ametys.message.MessageTarget.WORKFLOW_RESULT});
if (resultTarget)
{
Ametys.plugins.workflow.actions.WorkflowResultAction.editConditionalResult(resultTarget);
}
else
{
var propertyTarget = stepTarget.getSubtarget(function(target){return target.getId() == Ametys.message.MessageTarget.WORKFLOW_PROPERTY});
if (propertyTarget)
{
Ametys.plugins.workflow.actions.WorkflowEditionAction.editProperty(propertyTarget);
}
else
{
var actionTarget = stepTarget.getSubtarget(function(target){return target.getId() == Ametys.message.MessageTarget.WORKFLOW_ACTION});
if (actionTarget)
{
Ametys.plugins.workflow.actions.WorkflowTransitionAction.edit(actionTarget);
}
else
{
Ametys.plugins.workflow.actions.WorkflowStepAction.edit(stepTarget);
}
}
}
}
}
},
/**
* Delete an element of workflow
* @param {Ametys.plugins.workflow.controllers.EditOrDeleteElementController} controller The controller calling this function
*/
deleteElement: function(controller)
{
var stepTarget = Ametys.message.MessageTargetHelper.findTarget(controller.getMatchingTargets(), Ametys.message.MessageTarget.WORKFLOW_STEP);
var functionTarget = stepTarget.getSubtarget(function(target){return target.getId() == Ametys.message.MessageTarget.WORKFLOW_FUNCTION});
if (functionTarget)
{
Ametys.plugins.workflow.actions.WorkflowFunctionAction.deleteFunction(functionTarget);
}
else
{
var conditionTarget = stepTarget.getSubtarget(function(target){return target.getId() == Ametys.message.MessageTarget.WORKFLOW_CONDITION});
if (conditionTarget)
{
Ametys.plugins.workflow.actions.WorkflowConditionAction.deleteCondition(conditionTarget);
}
else
{
var operatorTarget = stepTarget.getSubtarget(function(target){return target.getId() == Ametys.message.MessageTarget.WORKFLOW_CONDITIONS_OPERATOR});
if (operatorTarget)
{
Ametys.plugins.workflow.actions.WorkflowConditionAction.deleteOperator(operatorTarget);
}
else
{
var resultTarget = stepTarget.getSubtarget(function(target){return target.getId() == Ametys.message.MessageTarget.WORKFLOW_RESULT});
if (resultTarget)
{
Ametys.plugins.workflow.actions.WorkflowResultAction.deleteConditionalResult(resultTarget);
}
else
{
var propertyTarget = stepTarget.getSubtarget(function(target){return target.getId() == Ametys.message.MessageTarget.WORKFLOW_PROPERTY});
if (propertyTarget)
{
Ametys.plugins.workflow.actions.WorkflowEditionAction.deleteProperty(propertyTarget);
}
else
{
var actionTarget = stepTarget.getSubtarget(function(target){return target.getId() == Ametys.message.MessageTarget.WORKFLOW_ACTION});
if (actionTarget)
{
Ametys.plugins.workflow.actions.WorkflowTransitionAction.removeTransition(actionTarget, stepTarget);
}
else
{
Ametys.plugins.workflow.actions.WorkflowStepAction.delete(stepTarget);
}
}
}
}
}
}
},
/**
* @private
* Callback of getWorkflowInfos set edit box value
* @param {Object} response the server response
* @param {Object} response.labels the multilingual labels defined for current workflow
*/
_getWorkflowLabelsCB: function(response)
{
if (!this._editWorkflowBox)
{
this._editWorkflowBox = this._createDialogBox();
}
this._editWorkflowBox.getComponent('label').setValue(response.labels);
this._editWorkflowBox.show();
},
/**
* Save the changes on current workflow
* @param {Ametys.plugins.workflow.controllers.SaveChangesController} controller The controller calling this function
*/
saveChanges: function(controller)
{
var target = Ametys.message.MessageTargetHelper.findTarget(controller.getMatchingTargets(), Ametys.message.MessageTarget.WORKFLOW_OBJECT);
if (!target)
{
return;
}
var workflowName = target.getParameters().id;
Ametys.plugins.workflow.dao.WorkflowsDAO.saveChanges([workflowName]);
},
/**
* Open confirm box to restore the workflow in the last saved state
* @param {Ametys.plugins.workflow.controllers.SaveChangesController} controller The controller calling this function
*/
reinit: function(controller)
{
var target = Ametys.message.MessageTargetHelper.findTarget(controller.getMatchingTargets(), Ametys.message.MessageTarget.WORKFLOW_OBJECT);
if (!target)
{
return;
}
var workflowName = target.getParameters().id;
Ametys.Msg.confirm("{{i18n UITOOL_WORKFLOW_EDITOR_REINIT_LABEL}}",
Ext.String.format("{{i18n PLUGINS_WORKFLOW_REINIT_CONFIRM}}"),
Ext.bind(this._doReinit, this, [workflowName], 1),
this
);
},
/**
* @private
* Restore the workflow in the last saved state
* @param {String} btn The pressed button. Can only be 'yes'/'no'
* @param {String} workflowName The workflow's unique name
*/
_doReinit: function(btn, workflowName)
{
if (btn == 'yes')
{
Ametys.plugins.workflow.dao.WorkflowsDAO.reinit([workflowName]);
}
},
/**
* Duplicate workflow in selection
* @param {Ametys.ribbon.element.ui.ButtonController} controller The controller calling this function
*/
duplicateWorkflow: function(controller)
{
var target = Ametys.message.MessageTargetHelper.findTarget(controller.getMatchingTargets(), Ametys.message.MessageTarget.WORKFLOW_OBJECT);
if (!target)
{
return;
}
this._mode = "duplicate";
this._duplicateId = target.getParameters().id;
Ametys.plugins.workflow.dao.WorkflowsDAO.getWorkflowInfos([this._duplicateId ], Ext.bind(this._setDuplicateId, this));
},
/**
* @private
* Callback of getWorkflowsDuplicateInfos, set field values and show duplication dialog box
* @param {Object} response the server response
* @param {String[]} response.workflowNames the list of unavailable workflow names
* @param {String[]} response.labels the workflow to duplicate's labels
*/
_setDuplicateId: function(response)
{
if (!this._duplicateWorkflowBox)
{
this._duplicateWorkflowBox = this._createDialogBox();
}
this._unavailableNames = response.workflowNames;
var increment = 1;
var id = this._duplicateId + "-" + increment;
while(this._unavailableNames.includes(id))
{
increment++;
id = this._duplicateId + "-" + increment;
}
this._duplicateWorkflowBox.getComponent('label').setValue(response.labels);
this._duplicateWorkflowBox.getComponent('id').setValue(id);
this._duplicateWorkflowBox.show();
}
});