/*
* Copyright 2024 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.
*/
/**
* Methods for parsing comments in a script to determine and ask the parameters
*/
Ext.define('Ametys.plugins.coreui.script.ScriptParameters',
{
singleton: true,
hasScriptParameters: function(script)
{
let re = new RegExp("/\\*\\$(.*?)\\*/", "gs");
let comment = re.exec(script);
return comment != null;
},
/**
* Display a parameter dialog if necessary
* @param {String} script The script containing potential parameters
* @param {Function} callback The callback to call when the parameters are valued
* @param {String} [title] The title of the dialog
* @param {String} [description] An introduction text to display in the dialog
* @param {Object} [existingValues] The existing values stored in a previous session to display in the dialog}
*/
askScriptParameters: function(script, callback, title, description, existingValues)
{
let conf = {};
// 1 - trouver un ensemble de /*$ */
let re = new RegExp("/\\*\\$(.*?)\\*/", "gs");
while (true)
{
let comment = re.exec(script);
if (!comment)
{
break;
}
let localConf = Ametys.plugins.coreui.script.ScriptParameters._parseComment(comment[1]);
if (localConf)
{
conf = {...conf, ...localConf};
}
}
if (Object.keys(conf).length == 0)
{
callback();
return;
}
let form = Ext.create("Ametys.form.ConfigurableFormPanel", {
border : false,
scrollable : false,
labelAlign: 'top',
defaultFieldConfig : {
labelWidth : 130,
anchor : '100%'
},
bodyStyle : {
paddingLeft : 0,
paddingTop: 0,
paddingBottom: 0
},
additionalWidgetsConf: {
standaloneEdition: true // standalone edition (use case: a popup without ribbon (for example richtext buttons) or FO edition)
},
displayGroupsDescriptions: false
});
form.configure(conf);
form.setValues(existingValues);
let dialog = Ext.create("Ametys.window.DialogBox", {
title: title || "{{i18n plugin.core-ui:PLUGINS_CORE_UI_TOOLS_SCRIPT_PARAMETERS_DIALOG_TITLE}}",
iconCls: "ametysicon-computer196",
width: 500,
maxHeight: 400,
scrollable: 'vertical',
layout: 'anchor',
defaultType: 'textfield',
defaults: {
cls: 'ametys',
labelAlign: 'right',
labelSeparator: '',
labelWidth: 130,
anchor: '100%',
style: 'margin-right:' + (Ametys.form.ConfigurableFormPanel.OFFSET_FIELDSET + 10 + Ametys.form.ConfigurableFormPanel.PADDING_TAB) + 'px'
},
items: [{
xtype: 'component',
itemId: 'helpmessage',
cls: 'a-text',
html: description || "{{i18n plugin.core-ui:PLUGINS_CORE_UI_TOOLS_SCRIPT_PARAMETERS_DIALOG_DESCRIPTION}}"
},
form,
],
selectDefaultFocus: true,
closeAction: 'destroy',
referenceHolder: true,
defaultButton: 'validate',
listeners:
{
scope: this,
close: function() { }
},
buttons : [{
reference: 'validate',
text :"{{i18n plugin.core-ui:PLUGINS_CORE_UI_TOOLS_SCRIPT_PARAMETERS_DIALOG_BUTTON_OK}}",
handler: Ext.bind(function() {
if (form.isValid())
{
let params = {};
let formValues = form.getJsonValues();
for (let paramName of Object.keys(formValues))
{
params[paramName] = {
type: form.getField(paramName).type, // should be the same as conf[paramName].type,
value: formValues[paramName]
};
}
callback(params);
dialog.close();
}
}, this),
scope: this
}, {
text :"{{i18n plugin.core-ui:PLUGINS_CORE_UI_TOOLS_SCRIPT_PARAMETERS_DIALOG_BUTTON_CANCEL}}",
handler: Ext.bind(function() {
dialog.close();
}, this)
}]
});
dialog.show();
},
__LINE_REGEXP: new RegExp(
"^"
+ "\\s*\\*?\\s*" // Optional leading *
+ "@param\\s+" // @param
+ "\\{([a-zA-Z0-9-]+)(\\[\\])?(;[^}]+)?\\}\\s+" // {Type[];params}
+ "(\\[?)" // Optionnal opening bracket
+ "([$_a-zA-Z][$_a-zA-Z0-9]*)" // Name
+ "(?:=(" // Optionnal default value
+ "null|undefined|" // Null or undefined
+ "true|false|" // Boolean
+ "\"[^\"]*\"|'[^']*'|" // String
+ "[0-9._]+|" // Long or Double
+ "\\[.*?\\]|\\{.*\\}" // Object or Array
+ "))?"
+ "(\\]?)" // Optionnal closing bracket
+ "(?:\\s+(.+))" // Description
+ "$"
),
__EMPTY_LINE_REGEXP: new RegExp("^\\s*\\*\\s*$"),
__TEXT_LINE_REGEXP: new RegExp("^\\s*\\*\\s*([^@]+)$"),
_parseComment: function(comment)
{
let conf = {};
let lastLocalConf = null;
// 2 - pour chaque ensemble, trouver chaque ligne
let params = comment.split(/\r?\n/);
for (let param of params)
{
if (param.trim().length > 0)
{
// 3 - pour chaque ligne, trouver le nom, la valeur par défaut, le type, la description...
let result;
if (result = Ametys.plugins.coreui.script.ScriptParameters.__EMPTY_LINE_REGEXP.exec(param))
{
// nothing
}
else if (result = Ametys.plugins.coreui.script.ScriptParameters.__TEXT_LINE_REGEXP.exec(param))
{
// description of the preceding if any
let text = result[1];
if (lastLocalConf)
{
lastLocalConf.description = text + (lastLocalConf.description ? "<br/>" + lastLocalConf.description : "");
}
}
else if (result = Ametys.plugins.coreui.script.ScriptParameters.__LINE_REGEXP.exec(param))
{
let type = result[1];
let multiple = result[2] == "[]";
let typeParams = result[3];
let openingBracket = result[4];
let name = result[5];
let defaultValue = result[6];
let closingBracket = result[7];
let label = result[8];
if (openingBracket && !closingBracket || !openingBracket && closingBracket)
{
Ametys.log.ErrorDialog.display({
title: "{{i18n PLUGINS_CORE_UI_TOOLS_SCRIPT_PARAMETERS}}",
text: "{{i18n PLUGINS_CORE_UI_TOOLS_SCRIPT_PARAMETERS_ERROR}}<br/><code>" + param + "</code>",
category: "Ametys.plugins.coreui.script.ScriptParameters"
});
throw new Error("Cannot parse the following param in variable comment (bracket issue): " + param);
}
let optional = openingBracket && closingBracket;
let params = {};
if (typeParams)
{
for (let p of typeParams.split(";"))
{
if (p.trim().length > 0)
{
let i = p.indexOf(":");
if (i == -1)
{
Ametys.log.ErrorDialog.display({
title: "{{i18n PLUGINS_CORE_UI_TOOLS_SCRIPT_PARAMETERS}}",
text: "{{i18n PLUGINS_CORE_UI_TOOLS_SCRIPT_PARAMETERS_ERROR}}<br/><code>" + param + "</code>",
category: "Ametys.plugins.coreui.script.ScriptParameters"
});
throw new Error("Cannot read the following type parameter: " + p);
}
params[p.substring(0, i)] = p.substring(i + 1);
}
}
}
lastLocalConf = Ametys.plugins.coreui.script.ScriptParameters._convertToConf(optional, multiple, defaultValue, type, params, label);
conf[name] = lastLocalConf;
}
else
{
Ametys.log.ErrorDialog.display({
title: "{{i18n PLUGINS_CORE_UI_TOOLS_SCRIPT_PARAMETERS}}",
text: "{{i18n PLUGINS_CORE_UI_TOOLS_SCRIPT_PARAMETERS_ERROR}}<br/><code>" + param + "</code>",
category: "Ametys.plugins.coreui.script.ScriptParameters"
});
throw new Error("Cannot parse the following line in variable comment: " + param);
}
}
}
return conf;
},
_convertToConf: function(optional, multiple, defaultValue, type, typeParams, description)
{
let lcType = type.toLowerCase();
let typedDefaultValue;
try
{
if (defaultValue !== undefined)
{
typedDefaultValue = JSON.parse(defaultValue);
}
}
catch (e)
{
Ametys.log.ErrorDialog.display({
title: "{{i18n PLUGINS_CORE_UI_TOOLS_SCRIPT_PARAMETERS_DEFAULTVALUE}}",
text: "{{i18n PLUGINS_CORE_UI_TOOLS_SCRIPT_PARAMETERS_DEFAULTVALUE_ERROR}}<br/><code>" + defaultValue + "</code>",
details: e,
category: "Ametys.plugins.coreui.script.ScriptParameters"
});
throw new Error("Error with default value " + defaultValue)
}
return {
"label": description,
"description": (!!defaultValue ? "{{i18n plugin.core-ui:PLUGINS_CORE_UI_TOOLS_SCRIPT_PARAMETERS_DIALOG_DEFAULTVALUE}} " + defaultValue : undefined),
"type": lcType,
"multiple": multiple,
"default-value": typedDefaultValue,
"widget": typeParams ? typeParams.widget : undefined,
"widget-params": typeParams,
"validation": {
"mandatory": !optional
}
}
}
});