N°6759 - Migrating to twig templating

This commit is contained in:
Timothee
2025-08-22 14:42:42 +02:00
parent 9c31a450dd
commit 338b47d8f6
9 changed files with 369 additions and 324 deletions

View File

@@ -5,7 +5,7 @@
*/
use Combodo\iTop\Application\UI\Base\Component\Alert\AlertUIBlockFactory;
use Combodo\iTop\Application\WebPage\iTopConfigEditorPage;
use Combodo\iTop\Config\Controller\ConfigEditorController;
use Combodo\iTop\Config\Validator\iTopConfigValidator;
require_once(APPROOT.'application/startup.inc.php');
@@ -17,98 +17,7 @@ LoginWebPage::DoLogin(); // Check user rights and prompt if needed
ApplicationMenu::CheckMenuIdEnabled('ConfigEditor');
$oP = new iTopConfigEditorPage();
$oLogCenterController = new ConfigEditorController();
$oLogCenterController->SetDefaultOperation('Edit');
$oLogCenterController->HandleOperation();
try {
$sOperation = utils::ReadParam('operation', '');
if (MetaModel::GetConfig()->Get('demo_mode')) {
throw new Exception(Dict::S('config-not-allowed-in-demo'), iTopConfigValidator::CONFIG_INFO);
}
if (MetaModel::GetModuleSetting('itop-config', 'config_editor', '') == 'disabled') {
throw new Exception(Dict::S('config-interactive-not-allowed'), iTopConfigValidator::CONFIG_WARNING);
}
$sConfigFile = APPROOT.'conf/'.utils::GetCurrentEnvironment().'/config-itop.php';
$sConfigContent = file_get_contents($sConfigFile);
$sConfigChecksum = md5($sConfigContent);
$sConfig = str_replace("\r\n", "\n", $sConfigContent);
$sOriginalConfig = $sConfig;
if (!empty($sOperation)) {
$sConfig = utils::ReadParam('new_config', '', false, 'raw_data');
}
try {
if ($sOperation == 'revert') {
throw new Exception(Dict::S('config-reverted'), iTopConfigValidator::CONFIG_WARNING);
}
if ($sOperation == 'save') {
$sTransactionId = utils::ReadParam('transaction_id', '', false, 'transaction_id');
if (!utils::IsTransactionValid($sTransactionId, true)) {
throw new Exception(Dict::S('config-error-transaction'), iTopConfigValidator::CONFIG_ERROR);
}
$sChecksum = utils::ReadParam('checksum');
if ($sChecksum !== $sConfigChecksum) {
throw new Exception(Dict::S('config-error-file-changed'), iTopConfigValidator::CONFIG_ERROR);
}
if ($sConfig === $sOriginalConfig) {
throw new Exception(Dict::S('config-no-change'), iTopConfigValidator::CONFIG_INFO);
}
$oValidator = new iTopConfigValidator();
$oValidator->Validate($sConfig);// throws exceptions
@chmod($sConfigFile, 0770); // Allow overwriting the file
$sTmpFile = tempnam(SetupUtils::GetTmpDir(), 'itop-cfg-');
// Don't write the file as-is since it would allow to inject any kind of PHP code.
// Instead, write the interpreted version of the file
// Note:
// The actual raw PHP code will anyhow be interpreted exactly twice: once in TestConfig() above
// and a second time during the load of the Config object below.
// If you are really concerned about an iTop administrator crafting some malicious
// PHP code inside the config file, then turn off the interactive configuration
// editor by adding the configuration parameter:
// 'itop-config' => array(
// 'config_editor' => 'disabled',
// )
file_put_contents($sTmpFile, $sConfig);
$oTempConfig = new Config($sTmpFile, true);
$oTempConfig->WriteToFile($sConfigFile);
@unlink($sTmpFile);
@chmod($sConfigFile, 0440); // Read-only
if ($oValidator->DBPasswordIsOk($oTempConfig->Get('db_pwd'))) {
$oAlert = AlertUIBlockFactory::MakeForSuccess('', Dict::S('config-saved'));
} else {
$oAlert = AlertUIBlockFactory::MakeForInformation('', Dict::S('config-saved-warning-db-password'));
}
$oP->AddUiBlock($oAlert);
$oP->CheckAsyncTasksRetryConfig($oTempConfig);
// Read the config from disk after save
$sConfigContent = file_get_contents($sConfigFile);
$sConfigChecksum = md5($sConfigContent);
$sConfig = str_replace("\r\n", "\n", $sConfigContent);
$sOriginalConfig = $sConfig;
}
}
catch (Exception $e) {
$oP->AddAlertFromException($e);
}
$oP->AddEditor($sOriginalConfig, $sOriginalConfig);
} catch (Exception $e) {
$oP->AddAlertFromException($e);
}
$oP->output();

View File

@@ -23,6 +23,7 @@ SetupWebPage::AddModule(
'src/Validator/iTopConfigAstValidator.php',
'src/Validator/iTopConfigSyntaxValidator.php',
'src/Validator/iTopConfigValidator.php',
'src/Controller/ConfigEditorController.php',
),
'webservice' => array(),
'dictionary' => array(

View File

@@ -0,0 +1,173 @@
<?php
namespace Combodo\iTop\Config\Controller;
use Combodo\iTop\Application\TwigBase\Controller\Controller;
use Combodo\iTop\Config\Validator\iTopConfigValidator;
use Config;
use Dict;
use Exception;
use MetaModel;
use SetupUtils;
use utils;
class ConfigEditorController extends Controller
{
public const ROUTE_NAMESPACE = 'config_editor';
public const MODULE_NAME = "itop-config";
private $aWarnings = [];
private $aInfo = [];
private $aErrors = [];
private $aSuccesses = [];
public function __construct() {
$sModuleName = 'itop-config';
parent::__construct(MODULESROOT.$sModuleName.'/templates', $sModuleName);
}
// /pages/UI.php?route=config_editor.edit
public function OperationEdit()
{
$bShowEditor = true;
$sConfigChecksum = '';
$sCurrentConfig = '';
try {
$sOperation = utils::ReadParam('edit_operation', '');
if (MetaModel::GetConfig()->Get('demo_mode')) {
throw new Exception(Dict::S('config-not-allowed-in-demo'), iTopConfigValidator::CONFIG_INFO);
}
if (MetaModel::GetModuleSetting('itop-config', 'config_editor', '') == 'disabled') {
throw new Exception(Dict::S('config-interactive-not-allowed'), iTopConfigValidator::CONFIG_WARNING);
}
$sConfigFile = APPROOT.'conf/'.utils::GetCurrentEnvironment().'/config-itop.php';
$sCurrentConfig = file_get_contents($sConfigFile);
$sConfigChecksum = md5($sCurrentConfig);
try {
if ($sOperation == 'revert') {
$this->AddAlert(Dict::S('config-reverted'), iTopConfigValidator::CONFIG_WARNING);
}
else if ($sOperation == 'save') {
$sTransactionId = utils::ReadParam('transaction_id', '', false, 'transaction_id');
if (!utils::IsTransactionValid($sTransactionId, true)) {
throw new Exception(Dict::S('config-error-transaction'), iTopConfigValidator::CONFIG_ERROR);
}
$sChecksum = utils::ReadParam('checksum');
if ($sChecksum !== $sConfigChecksum) {
throw new Exception(Dict::S('config-error-file-changed'), iTopConfigValidator::CONFIG_ERROR);
}
$sNewConfig = utils::ReadParam('new_config', '', false, 'raw_data');
$sNewConfig = str_replace("\r\n", "\n", $sNewConfig);
if ($sNewConfig === $sCurrentConfig) {
throw new Exception(Dict::S('config-no-change'), iTopConfigValidator::CONFIG_INFO);
}
$oValidator = new iTopConfigValidator();
$sNewConfig = str_replace("\r\n", "\n", $sNewConfig);
$oValidator->Validate($sNewConfig);// throws exceptions
@chmod($sConfigFile, 0770); // Allow overwriting the file
$sTmpFile = tempnam(SetupUtils::GetTmpDir(), 'itop-cfg-');
// Don't write the file as-is since it would allow to inject any kind of PHP code.
// Instead, write the interpreted version of the file
// Note:
// The actual raw PHP code will anyhow be interpreted exactly twice: once in TestConfig() above
// and a second time during the load of the Config object below.
// If you are really concerned about an iTop administrator crafting some malicious
// PHP code inside the config file, then turn off the interactive configuration
// editor by adding the configuration parameter:
// 'itop-config' => array(
// 'config_editor' => 'disabled',
// )
file_put_contents($sTmpFile, $sNewConfig);
$oTempConfig = new Config($sTmpFile, true);
$oTempConfig->WriteToFile($sConfigFile);
@unlink($sTmpFile);
@chmod($sConfigFile, 0440); // Read-only
if ($oValidator->DBPasswordIsOk($oTempConfig->Get('db_pwd'))) {
$this->AddAlert(Dict::S('config-saved'), iTopConfigValidator::CONFIG_SUCCESS);
} else {
$this->AddAlert(Dict::S('config-saved-warning-db-password'), iTopConfigValidator::CONFIG_INFO);
}
$this->AddAlert($oValidator->CheckAsyncTasksRetryConfig($oTempConfig), iTopConfigValidator::CONFIG_WARNING);
// Read the config from disk after save
$sCurrentConfig = file_get_contents($sConfigFile);
$sConfigChecksum = md5($sCurrentConfig);
}
}
catch (Exception $e) {
$this->AddAlertFromException($e);
}
$this->AddAceScripts();
}
catch (Exception $e) {
$bShowEditor = false;
$this->AddAlertFromException($e);
}
// display page
$this->DisplayPage([
'aErrors' => $this->aErrors,
'aWarnings' => $this->aWarnings,
'aNotices' => $this->aInfo,
'aSuccesses' => $this->aSuccesses,
'bShowEditor' => $bShowEditor,
'sTransactionId' => utils::GetNewTransactionId(),
'sChecksum' => $sConfigChecksum,
'sPrevConfig' => $sCurrentConfig,
'sNewConfig' => $sCurrentConfig,
]);
}
/**
* @return void
* @throws \Exception
*/
public function AddAceScripts(): void {
$sAceDir = 'node_modules/ace-builds/src-min/';
$this->AddLinkedScript(\utils::GetAbsoluteUrlAppRoot().$sAceDir.'ace.js');
$this->AddLinkedScript(\utils::GetAbsoluteUrlAppRoot().$sAceDir.'mode-php.js');
$this->AddLinkedScript(\utils::GetAbsoluteUrlAppRoot().$sAceDir.'theme-eclipse.js');
$this->AddLinkedScript(\utils::GetAbsoluteUrlAppRoot().$sAceDir.'ext-searchbox.js');
}
public function AddAlertFromException(Exception $e): void {
$this->AddAlert($e->getMessage(), $e->getCode());
}
public function AddAlert(array|string $sMessage, $iLevel): void {
if (is_array($sMessage)) {
foreach ($sMessage as $sSingleMessage) {
$this->AddAlert($sSingleMessage, $iLevel);
}
return;
}
switch ($iLevel) {
case iTopConfigValidator::CONFIG_SUCCESS :
$this->aSuccesses[] = $sMessage;
break;
case iTopConfigValidator::CONFIG_WARNING :
$this->aWarnings[] = $sMessage;
break;
case iTopConfigValidator::CONFIG_INFO :
$this->aInfo[] = $sMessage;
break;
default :
$this->aErrors[] = $sMessage;
};
}
}

View File

@@ -2,10 +2,14 @@
namespace Combodo\iTop\Config\Validator;
use AsyncTask;
use ReflectionClass;
class iTopConfigValidator {
const CONFIG_ERROR = 0;
const CONFIG_WARNING = 1;
const CONFIG_INFO = 2;
const CONFIG_SUCCESS = 3;
/**
* @param $sRawConfig
@@ -32,4 +36,24 @@ class iTopConfigValidator {
return true;
}
public function CheckAsyncTasksRetryConfig(\Config $oTempConfig): array
{
$aWarnings = [];
foreach (get_declared_classes() as $sPHPClass) {
$oRefClass = new ReflectionClass($sPHPClass);
if ($oRefClass->isSubclassOf('AsyncTask') && !$oRefClass->isAbstract()) {
$aMessages = AsyncTask::CheckRetryConfig($oTempConfig, $oRefClass->getName());
if (count($aMessages) !== 0) {
foreach ($aMessages as $sMessage) {
$aWarnings[] = $sMessage;
}
}
}
}
return $aWarnings;
}
}

View File

@@ -0,0 +1,32 @@
{% for sError in aErrors %}
{% UIAlert ForDanger{sTitle: sError} %}{% EndUIAlert %}
{% endfor %}
{% for sWarning in aWarnings %}
{% UIAlert ForWarning{sTitle: sWarning} %}{% EndUIAlert %}
{% endfor %}
{% for sNotice in aNotices %}
{% UIAlert ForInformation{sTitle: sNotice} %}{% EndUIAlert %}
{% endfor %}
{% for sSuccess in aSuccesses %}
{% UIAlert ForSuccess{sTitle: sSuccess} %}{% EndUIAlert %}
{% endfor %}
{% UITitle ForPage {'sTitle':'config-edit-title'|dict_s} %}{% EndUITitle %}
{#
{% UIButton ForPrimaryAction {'sLabel':'SysInfo:UI:DownloadReport'|dict_s, 'bIsSubmit':true} %}
{% UIInput ForHidden {sName:'operation', sValue:'SysInfoReport'} %}
#}
{% if bShowEditor %}
<p>{{ 'config-edit-intro'|dict_s }}</p>
{% UIForm Standard {} %}
{% UIButton ForCancel { 'sLabel':'config-cancel'|dict_s, 'bIsSubmit':true, 'sId':'cancel_button', 'sName':'edit_operation', 'sValue': 'revert'} %}
{% UIButton ForPrimaryAction {'sLabel':'config-apply'|dict_s, 'bIsSubmit':true, 'sId':'submit_button', 'sName':'edit_operation', 'sValue': 'save' } %}
{% UIInput ForHidden {'sName':'transaction_id', 'sValue':sTransactionId} %}
{% UIInput ForHidden {'sName':'checksum', 'sValue':sChecksum} %}
{% UIInput ForHidden {'sName':'prev_config', 'sValue':sPrevConfig} %}
{% UIInput ForHidden {'sName':'new_config', 'sValue':sNewConfig} %}
<div id ="new_config" style="position: absolute; top: 125px; bottom: 0; left: 5px; right: 5px;"></div>
{% EndUIForm %}
{% endif %}

View File

@@ -0,0 +1,135 @@
var EditorUtils = (function() {
var STORAGE_RANGE_KEY = 'cfgEditorRange';
var STORAGE_LINE_KEY = 'cfgEditorFirstline';
var _editorSavedRange = null;
var _editorSavedFirstLine = null;
var saveEditorDisplay = function(editor) {
_initObjectValues(editor);
_persistObjectValues();
};
var _initObjectValues = function(editor) {
_editorSavedRange = editor.getSelectionRange();
_editorSavedFirstLine = editor.renderer.getFirstVisibleRow();
};
var _persistObjectValues = function() {
sessionStorage.setItem(EditorUtils.STORAGE_RANGE_KEY, JSON.stringify(_editorSavedRange));
sessionStorage.setItem(EditorUtils.STORAGE_LINE_KEY, _editorSavedFirstLine);
};
var restoreEditorDisplay = function(editor) {
_restoreObjectValues();
_setEditorDisplay(editor);
};
var _restoreObjectValues = function() {
if ((sessionStorage.getItem(STORAGE_RANGE_KEY) == null)
|| (sessionStorage.getItem(STORAGE_LINE_KEY) == null)) {
return;
}
_editorSavedRange = JSON.parse(sessionStorage.getItem(EditorUtils.STORAGE_RANGE_KEY));
_editorSavedFirstLine = sessionStorage.getItem(EditorUtils.STORAGE_LINE_KEY);
sessionStorage.removeItem(STORAGE_RANGE_KEY);
sessionStorage.removeItem(STORAGE_LINE_KEY);
};
var _setEditorDisplay = function(editor) {
if ((_editorSavedRange == null) || (_editorSavedFirstLine == null)) {
return;
}
editor.selection.setRange(_editorSavedRange);
editor.renderer.scrollToRow(_editorSavedFirstLine);
};
var getEditorForm = function(editor) {
var editorContainer = $(editor.container);
return editorContainer.closest("form");
};
var updateConfigEditorButtonState = function(editor) {
var isSameContent = (editor.getValue() == $('#prev_config').val());
var hasNoError = $.isEmptyObject(editor.getSession().getAnnotations());
$('#cancel_button').prop('disabled', isSameContent);
$('#submit_button').prop('disabled', isSameContent || !hasNoError);
};
return {
STORAGE_RANGE_KEY: STORAGE_RANGE_KEY,
STORAGE_LINE_KEY : STORAGE_LINE_KEY,
saveEditorDisplay : saveEditorDisplay,
restoreEditorDisplay : restoreEditorDisplay,
getEditorForm : getEditorForm,
updateConfigEditorButtonState : updateConfigEditorButtonState
};
})();
var editor = ace.edit("new_config");
var configurationSource = $('input[name="new_config"]');
editor.getSession().setValue(configurationSource.val());
editor.getSession().on('change', function()
{
configurationSource.val(editor.getSession().getValue());
EditorUtils.updateConfigEditorButtonState(editor);
});
editor.getSession().on("changeAnnotation", function()
{
EditorUtils.updateConfigEditorButtonState(editor);
});
editor.setTheme("ace/theme/eclipse");
editor.getSession().setMode("ace/mode/php");
editor.commands.addCommand({
name: 'save',
bindKey: {win: "Ctrl-S", "mac": "Cmd-S"},
exec: function(editor) {
var editorForm = EditorUtils.getEditorForm(editor);
var submitButton = $('#submit_button');
if (submitButton.is(":enabled")) {
editorForm.trigger('submit');
}
}
});
var editorForm = EditorUtils.getEditorForm(editor);
editorForm.on('submit', function() {
EditorUtils.saveEditorDisplay(editor);
});
EditorUtils.restoreEditorDisplay(editor);
editor.focus();
//$sConfirmCancel = addslashes(Dict::S('config-confirm-cancel'));
function ResetConfig()
{
}
const repositionEditor = () => {
let oSubmitButton = document.getElementById('submit_button');
let iBottomPosition = oSubmitButton.offsetTop + oSubmitButton.offsetHeight + 10;
document.getElementById('new_config').style.top = iBottomPosition+"px";
};
repositionEditor();
document.getElementById('ibo-main-content').addEventListener('click',repositionEditor);
document.getElementById('cancel_button').onclick = ()=>{
$("#operation").attr('value', 'revert');
if (confirm({{ 'config-confirm-cancel'|dict_s|json_encode|raw }})) {
$('input[name="new_config"]').val($('input[name="prev_config"]').val());
return true;
}
return false;
};

View File

@@ -386,7 +386,6 @@ return array(
'Combodo\\iTop\\Application\\WebPage\\WebPage' => $baseDir . '/sources/Application/WebPage/WebPage.php',
'Combodo\\iTop\\Application\\WebPage\\XMLPage' => $baseDir . '/sources/Application/WebPage/XMLPage.php',
'Combodo\\iTop\\Application\\WebPage\\iTabbedPage' => $baseDir . '/sources/Application/WebPage/iTabbedPage.php',
'Combodo\\iTop\\Application\\WebPage\\iTopConfigEditorPage' => $baseDir . '/sources/Application/WebPage/iTopConfigEditorPage.php',
'Combodo\\iTop\\Application\\WebPage\\iTopPDF' => $baseDir . '/sources/Application/WebPage/iTopPDF.php',
'Combodo\\iTop\\Application\\WebPage\\iTopWebPage' => $baseDir . '/sources/Application/WebPage/iTopWebPage.php',
'Combodo\\iTop\\Application\\WebPage\\iTopWizardWebPage' => $baseDir . '/sources/Application/WebPage/iTopWizardWebPage.php',

View File

@@ -764,7 +764,6 @@ class ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f
'Combodo\\iTop\\Application\\WebPage\\WebPage' => __DIR__ . '/../..' . '/sources/Application/WebPage/WebPage.php',
'Combodo\\iTop\\Application\\WebPage\\XMLPage' => __DIR__ . '/../..' . '/sources/Application/WebPage/XMLPage.php',
'Combodo\\iTop\\Application\\WebPage\\iTabbedPage' => __DIR__ . '/../..' . '/sources/Application/WebPage/iTabbedPage.php',
'Combodo\\iTop\\Application\\WebPage\\iTopConfigEditorPage' => __DIR__ . '/../..' . '/sources/Application/WebPage/iTopConfigEditorPage.php',
'Combodo\\iTop\\Application\\WebPage\\iTopPDF' => __DIR__ . '/../..' . '/sources/Application/WebPage/iTopPDF.php',
'Combodo\\iTop\\Application\\WebPage\\iTopWebPage' => __DIR__ . '/../..' . '/sources/Application/WebPage/iTopWebPage.php',
'Combodo\\iTop\\Application\\WebPage\\iTopWizardWebPage' => __DIR__ . '/../..' . '/sources/Application/WebPage/iTopWizardWebPage.php',

View File

@@ -1,227 +0,0 @@
<?php
namespace Combodo\iTop\Application\WebPage;
use AsyncTask;
use Combodo\iTop\Config\Validator\iTopConfigValidator;
use Dict;
use ReflectionClass;
use utils;
use Combodo\iTop\Application\UI\Base\Component\Alert\AlertUIBlockFactory;
use Combodo\iTop\Application\UI\Base\Component\Button\ButtonUIBlockFactory;
use Combodo\iTop\Application\UI\Base\Component\Form\Form;
use Combodo\iTop\Application\UI\Base\Component\Html\Html;
use Combodo\iTop\Application\UI\Base\Component\Input\InputUIBlockFactory;
use Combodo\iTop\Application\UI\Base\Component\Title\TitleUIBlockFactory;
class iTopConfigEditorPage extends iTopWebPage
{
public function __construct()
{
parent::__construct(Dict::S('modules-config-edit-title'));
$this->set_base(utils::GetAbsoluteUrlAppRoot().'pages/');
$sAceDir = 'node_modules/ace-builds/src-min/';
$this->LinkScriptFromAppRoot($sAceDir.'ace.js');
$this->LinkScriptFromAppRoot($sAceDir.'mode-php.js');
$this->LinkScriptFromAppRoot($sAceDir.'theme-eclipse.js');
$this->LinkScriptFromAppRoot($sAceDir.'ext-searchbox.js');
$this->AddUiBlock(TitleUIBlockFactory::MakeForPage(Dict::S('config-edit-title')));
}
public function AddAlertFromException(\Exception $e)
{
$oAlert = match ($e->getCode()) {
iTopConfigValidator::CONFIG_WARNING => AlertUIBlockFactory::MakeForWarning('', $e->getMessage()),
iTopConfigValidator::CONFIG_INFO => AlertUIBlockFactory::MakeForInformation('', $e->getMessage()),
default => AlertUIBlockFactory::MakeForDanger('', $e->getMessage()),
};
$this->AddUiBlock($oAlert);
}
public function AddConfigScripts() {
$this->add_script(<<<JS
var EditorUtils = (function() {
var STORAGE_RANGE_KEY = 'cfgEditorRange';
var STORAGE_LINE_KEY = 'cfgEditorFirstline';
var _editorSavedRange = null;
var _editorSavedFirstLine = null;
var saveEditorDisplay = function(editor) {
_initObjectValues(editor);
_persistObjectValues();
};
var _initObjectValues = function(editor) {
_editorSavedRange = editor.getSelectionRange();
_editorSavedFirstLine = editor.renderer.getFirstVisibleRow();
};
var _persistObjectValues = function() {
sessionStorage.setItem(EditorUtils.STORAGE_RANGE_KEY, JSON.stringify(_editorSavedRange));
sessionStorage.setItem(EditorUtils.STORAGE_LINE_KEY, _editorSavedFirstLine);
};
var restoreEditorDisplay = function(editor) {
_restoreObjectValues();
_setEditorDisplay(editor);
};
var _restoreObjectValues = function() {
if ((sessionStorage.getItem(STORAGE_RANGE_KEY) == null)
|| (sessionStorage.getItem(STORAGE_LINE_KEY) == null)) {
return;
}
_editorSavedRange = JSON.parse(sessionStorage.getItem(EditorUtils.STORAGE_RANGE_KEY));
_editorSavedFirstLine = sessionStorage.getItem(EditorUtils.STORAGE_LINE_KEY);
sessionStorage.removeItem(STORAGE_RANGE_KEY);
sessionStorage.removeItem(STORAGE_LINE_KEY);
};
var _setEditorDisplay = function(editor) {
if ((_editorSavedRange == null) || (_editorSavedFirstLine == null)) {
return;
}
editor.selection.setRange(_editorSavedRange);
editor.renderer.scrollToRow(_editorSavedFirstLine);
};
var getEditorForm = function(editor) {
var editorContainer = $(editor.container);
return editorContainer.closest("form");
};
var updateConfigEditorButtonState = function(editor) {
var isSameContent = (editor.getValue() == $('#prev_config').val());
var hasNoError = $.isEmptyObject(editor.getSession().getAnnotations());
$('#cancel_button').prop('disabled', isSameContent);
$('#submit_button').prop('disabled', isSameContent || !hasNoError);
};
return {
STORAGE_RANGE_KEY: STORAGE_RANGE_KEY,
STORAGE_LINE_KEY : STORAGE_LINE_KEY,
saveEditorDisplay : saveEditorDisplay,
restoreEditorDisplay : restoreEditorDisplay,
getEditorForm : getEditorForm,
updateConfigEditorButtonState : updateConfigEditorButtonState
};
})();
JS
);
$this->add_ready_script(<<<JS
let oSubmitButton = document.getElementById('submit_button');
let iBottomPosition = oSubmitButton.offsetTop + oSubmitButton.offsetHeight + 10;
document.getElementById('new_config').style.top = iBottomPosition+"px";
console.log('Set config '+iBottomPosition+' from top');
var editor = ace.edit("new_config");
var configurationSource = $('input[name="new_config"]');
editor.getSession().setValue(configurationSource.val());
editor.getSession().on('change', function()
{
configurationSource.val(editor.getSession().getValue());
EditorUtils.updateConfigEditorButtonState(editor);
});
editor.getSession().on("changeAnnotation", function()
{
EditorUtils.updateConfigEditorButtonState(editor);
});
editor.setTheme("ace/theme/eclipse");
editor.getSession().setMode("ace/mode/php");
editor.commands.addCommand({
name: 'save',
bindKey: {win: "Ctrl-S", "mac": "Cmd-S"},
exec: function(editor) {
var editorForm = EditorUtils.getEditorForm(editor);
var submitButton = $('#submit_button');
if (submitButton.is(":enabled")) {
editorForm.trigger('submit');
}
}
});
var editorForm = EditorUtils.getEditorForm(editor);
editorForm.on('submit', function() {
EditorUtils.saveEditorDisplay(editor);
});
EditorUtils.restoreEditorDisplay(editor);
editor.focus();
JS
);
$sConfirmCancel = addslashes(Dict::S('config-confirm-cancel'));
$this->add_script(<<<JS
function ResetConfig()
{
$("#operation").attr('value', 'revert');
if (confirm('$sConfirmCancel'))
{
$('input[name="new_config"]').val(prevConfig.val());
return true;
}
return false;
}
JS
);
}
public function CheckAsyncTasksRetryConfig(\Config $oTempConfig)
{
foreach (get_declared_classes() as $sPHPClass) {
$oRefClass = new ReflectionClass($sPHPClass);
if ($oRefClass->isSubclassOf('AsyncTask') && !$oRefClass->isAbstract()) {
$aMessages = AsyncTask::CheckRetryConfig($oTempConfig, $oRefClass->getName());
if (count($aMessages) !== 0) {
foreach ($aMessages as $sMessage) {
$oAlert = AlertUIBlockFactory::MakeForWarning('', $sMessage);
$this->AddUiBlock($oAlert);
}
}
}
}
}
public function AddEditor(string $sPrevConfig, string $sNewConfig) {
$sConfigChecksum = md5($sPrevConfig);
// (remove EscapeHtml) N°5914 - Wrong encoding in modules configuration editor
$this->AddUiBlock(new Html('<p>'.Dict::S('config-edit-intro').'</p>'));
$oForm = new Form();
$oForm->AddSubBlock(InputUIBlockFactory::MakeForHidden('operation', 'save', 'operation'));
$oForm->AddSubBlock(InputUIBlockFactory::MakeForHidden('transaction_id', utils::GetNewTransactionId()));
$oForm->AddSubBlock(InputUIBlockFactory::MakeForHidden('checksum', $sConfigChecksum));
//--- Cancel button
$oCancelButton = ButtonUIBlockFactory::MakeForCancel(Dict::S('config-cancel'), 'cancel_button', null, true, 'cancel_button');
$oCancelButton->SetOnClickJsCode("return ResetConfig();");
$oForm->AddSubBlock($oCancelButton);
//--- Submit button
$oSubmitButton = ButtonUIBlockFactory::MakeForPrimaryAction(Dict::S('config-apply'), null, Dict::S('config-apply'), true, 'submit_button');
$oForm->AddSubBlock($oSubmitButton);
//--- Config editor
$oForm->AddSubBlock(InputUIBlockFactory::MakeForHidden('prev_config', $sPrevConfig, 'prev_config'));
$oForm->AddSubBlock(InputUIBlockFactory::MakeForHidden('new_config', $sNewConfig));
$oForm->AddHtml("<div id =\"new_config\" style=\"position: absolute; top: 125px; bottom: 0; left: 5px; right: 5px;\"></div>");
$this->AddUiBlock($oForm);
$this->AddConfigScripts();
}
}