mirror of
https://github.com/Combodo/iTop.git
synced 2026-02-13 07:24:13 +01:00
Configuration file editor:
- support syntax highlighting and checking (ace editor) - better "apply" and "reset" buttons management - limit code injection when checking the configuration - better syntax checking for PHP7 SVN:trunk[4926]
This commit is contained in:
@@ -39,13 +39,16 @@ function TestConfig($sContents, $oP)
|
||||
{
|
||||
ini_set('display_errors', 1);
|
||||
ob_start();
|
||||
eval('?'.'>'.trim($sContents));
|
||||
$sNoise = trim(ob_get_contents());
|
||||
// in PHP < 7.0.0 syntax errors are in output
|
||||
// in PHP >= 7.0.0 syntax errors are thrown as Error
|
||||
$sSafeContent = preg_replace(array('#^\s*<\?php#', '#\?>\s*$#'), '', $sContents);
|
||||
eval('if(0){'.trim($sSafeContent).'}');
|
||||
$sNoise = trim(ob_get_contents());
|
||||
ob_end_clean();
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
// well, never reach in case of parsing error :-(
|
||||
}
|
||||
catch (Error $e)
|
||||
{
|
||||
// ParseError only thrown in PHP7
|
||||
throw new Exception('Error in configuration: '.$e->getMessage());
|
||||
}
|
||||
if (strlen($sNoise) > 0)
|
||||
@@ -62,14 +65,6 @@ function TestConfig($sContents, $oP)
|
||||
for ($i = 0 ; $i < $iLine - 1; $i++) $iStart += strlen($aLines[$i]);
|
||||
$iEnd = $iStart + strlen($aLines[$iLine - 1]);
|
||||
$iTotalLines = count($aLines);
|
||||
$oP->add_ready_script(
|
||||
<<<EOF
|
||||
setCursorPos($('#new_config')[0], $iStart, $iEnd);
|
||||
$('#new_config')[0].focus();
|
||||
var iScroll = Math.floor($('#new_config')[0].scrollHeight * ($iLine - 20) / $iTotalLines);
|
||||
$('#new_config').scrollTop(iScroll);
|
||||
EOF
|
||||
);
|
||||
|
||||
$sMessage = Dict::Format('config-parse-error', $sMessage, $sLine);
|
||||
throw new Exception($sMessage);
|
||||
@@ -93,7 +88,11 @@ LoginWebPage::DoLogin(true); // Check user rights and prompt if needed (must be
|
||||
|
||||
$oP = new iTopWebPage(Dict::S('config-edit-title'));
|
||||
$oP->set_base(utils::GetAbsoluteUrlAppRoot().'pages/');
|
||||
|
||||
$oP->add_linked_script(utils::GetCurrentModuleUrl().'/js/ace.js');
|
||||
$oP->add_linked_script(utils::GetCurrentModuleUrl().'/js/mode-php.js');
|
||||
$oP->add_linked_script(utils::GetCurrentModuleUrl().'/js/theme-eclipse.js');
|
||||
$oP->add_linked_script(utils::GetCurrentModuleUrl().'/js/ext-searchbox.js');
|
||||
//$oP->add_linked_script(utils::GetCurrentModuleUrl().'/js/ext-textarea.js');
|
||||
|
||||
try
|
||||
{
|
||||
@@ -111,134 +110,129 @@ try
|
||||
}
|
||||
else
|
||||
{
|
||||
$oP->add_style(
|
||||
<<<EOF
|
||||
textarea {
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
|
||||
width: 100%;
|
||||
height: 550px;
|
||||
}
|
||||
.current_line {
|
||||
display: none;
|
||||
margin-left: 20px;
|
||||
}
|
||||
EOF
|
||||
);
|
||||
|
||||
$sConfigFile = APPROOT.'conf/'.utils::GetCurrentEnvironment().'/config-itop.php';
|
||||
|
||||
if ($sOperation == 'save')
|
||||
{
|
||||
$sConfig = utils::ReadParam('new_config', '', false, 'raw_data');
|
||||
$sTransactionId = utils::ReadParam('transaction_id', '');
|
||||
$sOrginalConfig = utils::ReadParam('prev_config', '', false, 'raw_data');
|
||||
if (!utils::IsTransactionValid($sTransactionId, true))
|
||||
{
|
||||
$oP->add("<div class=\"header_message message_info\">Error: invalid Transaction ID. The configuration was <b>NOT</b> modified.</div>");
|
||||
}
|
||||
else
|
||||
{
|
||||
if ($sConfig == $sOrginalConfig)
|
||||
{
|
||||
$oP->add('<div id="save_result" class="header_message">'.Dict::S('config-no-change').'</div>');
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
TestConfig($sConfig, $oP); // 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, 0444); // Read-only
|
||||
|
||||
$oP->p('<div id="save_result" class="header_message message_ok">'.Dict::S('Successfully recorded.').'</div>');
|
||||
$sOrginalConfig = str_replace("\r\n", "\n", file_get_contents($sConfigFile));
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
$oP->p('<div id="save_result" class="header_message message_error">'.$e->getMessage().'</div>');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$sConfig = str_replace("\r\n", "\n", file_get_contents($sConfigFile));
|
||||
$sOrginalConfig = $sConfig;
|
||||
}
|
||||
|
||||
|
||||
$iEditorTopMargin = 9;
|
||||
$sConfig = str_replace("\r\n", "\n", file_get_contents($sConfigFile));
|
||||
$sOrginalConfig = $sConfig;
|
||||
|
||||
if (!empty($sOperation))
|
||||
{
|
||||
$iEditorTopMargin = 14;
|
||||
$sConfig = utils::ReadParam('new_config', '', false, 'raw_data');
|
||||
$sOrginalConfig = utils::ReadParam('prev_config', '', false, 'raw_data');
|
||||
}
|
||||
|
||||
if ($sOperation == 'revert')
|
||||
{
|
||||
$oP->add('<div id="save_result" class="header_message message_info">'.Dict::S('config-reverted').'</div>');
|
||||
}
|
||||
if ($sOperation == 'save')
|
||||
{
|
||||
$sTransactionId = utils::ReadParam('transaction_id', '');
|
||||
if (!utils::IsTransactionValid($sTransactionId, true))
|
||||
{
|
||||
$oP->add("<div class=\"header_message message_info\">Error: invalid Transaction ID. The configuration was <b>NOT</b> modified.</div>");
|
||||
}
|
||||
else
|
||||
{
|
||||
if ($sConfig == $sOrginalConfig)
|
||||
{
|
||||
$oP->add('<div id="save_result" class="header_message">'.Dict::S('config-no-change').'</div>');
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
TestConfig($sConfig, $oP); // 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, 0444); // Read-only
|
||||
|
||||
$oP->p('<div id="save_result" class="header_message message_ok">'.Dict::S('config-saved').'</div>');
|
||||
$sOrginalConfig = str_replace("\r\n", "\n", file_get_contents($sConfigFile));
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
$oP->p('<div id="save_result" class="header_message message_error">'.$e->getMessage().'</div>');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$sConfigEscaped = htmlentities($sConfig, ENT_QUOTES, 'UTF-8');
|
||||
$sOriginalConfigEscaped = htmlentities($sOrginalConfig, ENT_QUOTES, 'UTF-8');
|
||||
$oP->p(Dict::S('config-edit-intro'));
|
||||
$oP->add("<form method=\"POST\">");
|
||||
$oP->add("<input type=\"hidden\" name=\"operation\" value=\"save\">");
|
||||
$oP->add("<input id=\"operation\" type=\"hidden\" name=\"operation\" value=\"save\">");
|
||||
$oP->add("<input type=\"hidden\" name=\"transaction_id\" value=\"".utils::GetNewTransactionId()."\">");
|
||||
$oP->add("<input type=\"submit\" value=\"".Dict::S('config-apply')."\"><button onclick=\"ResetConfig(); return false;\">".Dict::S('config-cancel')."</button>");
|
||||
$oP->add("<span class=\"current_line\">".Dict::Format('config-current-line', "<span class=\"line_number\"></span>")."</span>");
|
||||
$oP->add("<input id=\"submit_button\" type=\"submit\" value=\"".Dict::S('config-apply')."\"><button id=\"cancel_button\" disabled=\"disabled\" onclick=\"return ResetConfig();\">".Dict::S('config-cancel')."</button>");
|
||||
$oP->add("<input type=\"hidden\" id=\"prev_config\" name=\"prev_config\" value=\"$sOriginalConfigEscaped\">");
|
||||
$oP->add("<textarea id =\"new_config\" name=\"new_config\" onkeyup=\"UpdateLineNumber();\" onmouseup=\"UpdateLineNumber();\">$sConfigEscaped</textarea>");
|
||||
$oP->add("<input type=\"submit\" value=\"".Dict::S('config-apply')."\"><button onclick=\"ResetConfig(); return false;\">".Dict::S('config-cancel')."</button>");
|
||||
$oP->add("<span class=\"current_line\">".Dict::Format('config-current-line', "<span class=\"line_number\"></span>")."</span>");
|
||||
$oP->add("<input type=\"hidden\" name=\"new_config\" value=\"$sConfigEscaped\">");
|
||||
$oP->add("<div id =\"new_config\" style=\"position: absolute; top: ".$iEditorTopMargin."em; bottom: 0; left: 5px; right: 5px;\"></div>");
|
||||
$oP->add("</form>");
|
||||
|
||||
$sConfirmCancel = addslashes(Dict::S('config-confirm-cancel'));
|
||||
$oP->add_ready_script(
|
||||
<<<EOF
|
||||
var editor = ace.edit("new_config");
|
||||
var textarea = $('input[name="new_config"]');
|
||||
editor.getSession().setValue(textarea.val());
|
||||
editor.getSession().on('change', function()
|
||||
{
|
||||
textarea.val(editor.getSession().getValue());
|
||||
UpdateConfigEditorButtonState();
|
||||
});
|
||||
editor.getSession().on("changeAnnotation", function()
|
||||
{
|
||||
UpdateConfigEditorButtonState();
|
||||
});
|
||||
editor.setTheme("ace/theme/eclipse");
|
||||
editor.getSession().setMode("ace/mode/php");
|
||||
function UpdateConfigEditorButtonState()
|
||||
{
|
||||
var editor = ace.edit("new_config");
|
||||
var isSameContent = editor.getValue() == $('#prev_config').val();
|
||||
var hasNoError = $.isEmptyObject(editor.getSession().getAnnotations());
|
||||
$('#cancel_button').attr('disabled', isSameContent);
|
||||
$('#submit_button').attr('disabled', isSameContent || !hasNoError);
|
||||
}
|
||||
EOF
|
||||
);
|
||||
|
||||
$oP->add_script(
|
||||
<<<EOF
|
||||
function UpdateLineNumber()
|
||||
{
|
||||
var oTextArea = $('#new_config')[0];
|
||||
$('.line_number').html(oTextArea.value.substr(0, oTextArea.selectionStart).split("\\n").length);
|
||||
$('.current_line').show();
|
||||
}
|
||||
function ResetConfig()
|
||||
{
|
||||
if ($('#new_config').val() != $('#prev_config').val())
|
||||
var editor = ace.edit("new_config");
|
||||
$("#operation").attr('value', 'revert');
|
||||
if (editor.getValue() != $('#prev_config').val())
|
||||
{
|
||||
if (confirm('$sConfirmCancel'))
|
||||
{
|
||||
$('#new_config').val($('#prev_config').val());
|
||||
$('input[name="new_config"]').val($('#prev_config').val());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
$('.current_line').hide();
|
||||
$('#save_result').hide();
|
||||
return false;
|
||||
}
|
||||
|
||||
function setCursorPos(input, start, end) {
|
||||
if (arguments.length < 3) end = start;
|
||||
if ("selectionStart" in input) {
|
||||
setTimeout(function() {
|
||||
input.selectionStart = start;
|
||||
input.selectionEnd = end;
|
||||
}, 1);
|
||||
}
|
||||
else if (input.createTextRange) {
|
||||
var rng = input.createTextRange();
|
||||
rng.moveStart("character", start);
|
||||
rng.collapse();
|
||||
rng.moveEnd("character", end - start);
|
||||
rng.select();
|
||||
}
|
||||
}
|
||||
EOF
|
||||
);
|
||||
}
|
||||
|
||||
@@ -14,8 +14,10 @@ Dict::Add('CS CZ', 'Czech', 'Čeština', array(
|
||||
'config-edit-intro' => 'Při úpravách konfiguračního souboru buďte velice opatrní. Nesprávné nastavení může vést k nedostupnosti iTop', //In particular, only the upper items (i.e. the global configuration and modules settings) should be edited.
|
||||
'config-apply' => 'Použít',
|
||||
'config-cancel' => 'Zrušit',
|
||||
'config-saved' => 'Successfully recorded.~~',
|
||||
'config-confirm-cancel' => 'Vaše úpravy nebudou uloženy.',
|
||||
'config-no-change' => 'Soubor nebyl změněn.',
|
||||
'config-reverted' => 'The configuration has been reverted.~~',
|
||||
'config-parse-error' => 'Řádek %2$d: %1$s.<br/>Soubor nebyl uložen.',
|
||||
'config-current-line' => 'Řádek: %1$s',
|
||||
));
|
||||
|
||||
@@ -30,8 +30,10 @@ Dict::Add('DE DE', 'German', 'Deutsch', array(
|
||||
'config-edit-intro' => 'Seien sie bei der Bearbeitung der Konfigurationsdatei sehr vorsichtig.',
|
||||
'config-apply' => 'Anwenden',
|
||||
'config-cancel' => 'Zurücksetzen',
|
||||
'config-saved' => 'Successfully recorded.~~',
|
||||
'config-confirm-cancel' => 'Ihre Änderungen werden nicht gespeichert.',
|
||||
'config-no-change' => 'Keine Änderungen: Die Datei wurde nicht verändert.',
|
||||
'config-reverted' => 'The configuration has been reverted.~~',
|
||||
'config-parse-error' => 'Zeile %2$d: %1$s.<br/>Die Datei wurde nicht aktualisiert.',
|
||||
'config-current-line' => 'Editiere Zeile: %1$s',
|
||||
));
|
||||
|
||||
@@ -13,8 +13,10 @@ Dict::Add('EN US', 'English', 'English', array(
|
||||
'config-edit-intro' => 'Be very cautious when editing the configuration file.',
|
||||
'config-apply' => 'Apply',
|
||||
'config-cancel' => 'Reset',
|
||||
'config-saved' => 'Successfully recorded.',
|
||||
'config-confirm-cancel' => 'Your changes will be lost.',
|
||||
'config-no-change' => 'No change: the file has been left unchanged.',
|
||||
'config-reverted' => 'The configuration has been reverted.',
|
||||
'config-parse-error' => 'Line %2$d: %1$s.<br/>The file has NOT been updated.',
|
||||
'config-current-line' => 'Editing line: %1$s',
|
||||
));
|
||||
|
||||
@@ -13,8 +13,10 @@ Dict::Add('FR FR', 'French', 'Français', array(
|
||||
'config-edit-intro' => 'Attention: une configuration incorrecte peut rendre iTop inopérant pour tous les utilisateurs!',
|
||||
'config-save' => 'Appliquer',
|
||||
'config-restore' => 'Réinitialiser',
|
||||
'config-saved' => 'Configuration enregistrée.',
|
||||
'config-confirm-cancel' => 'Vos modifications seront perdues.',
|
||||
'config-no-change' => 'Aucun changement : le fichier n\'a pas été altéré.',
|
||||
'config-reverted' => 'Vos modifications ont été écrasées par la version enregistrée.',
|
||||
'config-parse-error' => 'Ligne %2$d: %1$s.<br/>Le fichier n\'a PAS été modifié.',
|
||||
'config-current-line' => 'Ligne en édition : %1$s',
|
||||
'config-apply' => 'Enregistrer',
|
||||
|
||||
14
datamodels/2.x/itop-config/js/ace.js
Normal file
14
datamodels/2.x/itop-config/js/ace.js
Normal file
File diff suppressed because one or more lines are too long
4
datamodels/2.x/itop-config/js/ext-searchbox.js
Normal file
4
datamodels/2.x/itop-config/js/ext-searchbox.js
Normal file
File diff suppressed because one or more lines are too long
1
datamodels/2.x/itop-config/js/mode-php.js
Normal file
1
datamodels/2.x/itop-config/js/mode-php.js
Normal file
File diff suppressed because one or more lines are too long
1
datamodels/2.x/itop-config/js/theme-eclipse.js
Normal file
1
datamodels/2.x/itop-config/js/theme-eclipse.js
Normal file
@@ -0,0 +1 @@
|
||||
ace.define("ace/theme/eclipse",["require","exports","module","ace/lib/dom"],function(e,t,n){"use strict";t.isDark=!1,t.cssText='.ace-eclipse .ace_gutter {background: #ebebeb;border-right: 1px solid rgb(159, 159, 159);color: rgb(136, 136, 136);}.ace-eclipse .ace_print-margin {width: 1px;background: #ebebeb;}.ace-eclipse {background-color: #FFFFFF;color: black;}.ace-eclipse .ace_fold {background-color: rgb(60, 76, 114);}.ace-eclipse .ace_cursor {color: black;}.ace-eclipse .ace_storage,.ace-eclipse .ace_keyword,.ace-eclipse .ace_variable {color: rgb(127, 0, 85);}.ace-eclipse .ace_constant.ace_buildin {color: rgb(88, 72, 246);}.ace-eclipse .ace_constant.ace_library {color: rgb(6, 150, 14);}.ace-eclipse .ace_function {color: rgb(60, 76, 114);}.ace-eclipse .ace_string {color: rgb(42, 0, 255);}.ace-eclipse .ace_comment {color: rgb(113, 150, 130);}.ace-eclipse .ace_comment.ace_doc {color: rgb(63, 95, 191);}.ace-eclipse .ace_comment.ace_doc.ace_tag {color: rgb(127, 159, 191);}.ace-eclipse .ace_constant.ace_numeric {color: darkblue;}.ace-eclipse .ace_tag {color: rgb(25, 118, 116);}.ace-eclipse .ace_type {color: rgb(127, 0, 127);}.ace-eclipse .ace_xml-pe {color: rgb(104, 104, 91);}.ace-eclipse .ace_marker-layer .ace_selection {background: rgb(181, 213, 255);}.ace-eclipse .ace_marker-layer .ace_bracket {margin: -1px 0 0 -1px;border: 1px solid rgb(192, 192, 192);}.ace-eclipse .ace_meta.ace_tag {color:rgb(25, 118, 116);}.ace-eclipse .ace_invisible {color: #ddd;}.ace-eclipse .ace_entity.ace_other.ace_attribute-name {color:rgb(127, 0, 127);}.ace-eclipse .ace_marker-layer .ace_step {background: rgb(255, 255, 0);}.ace-eclipse .ace_active-line {background: rgb(232, 242, 254);}.ace-eclipse .ace_gutter-active-line {background-color : #DADADA;}.ace-eclipse .ace_marker-layer .ace_selected-word {border: 1px solid rgb(181, 213, 255);}.ace-eclipse .ace_indent-guide {background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAACCAYAAACZgbYnAAAAE0lEQVQImWP4////f4bLly//BwAmVgd1/w11/gAAAABJRU5ErkJggg==") right repeat-y;}',t.cssClass="ace-eclipse";var r=e("../lib/dom");r.importCssString(t.cssText,t.cssClass)})
|
||||
1
datamodels/2.x/itop-config/js/worker-php.js
Normal file
1
datamodels/2.x/itop-config/js/worker-php.js
Normal file
File diff suppressed because one or more lines are too long
35
datamodels/2.x/itop-config/license.itop-config.xml
Normal file
35
datamodels/2.x/itop-config/license.itop-config.xml
Normal file
@@ -0,0 +1,35 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<licenses>
|
||||
<license>
|
||||
<product>ajaxorg/ace</product>
|
||||
<author>Ajax.org B.V.</author>
|
||||
<license_type>BSD 3-clause "New" or "Revised"</license_type>
|
||||
<text><![CDATA[
|
||||
Copyright (c) 2010, Ajax.org B.V.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of Ajax.org B.V. nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
]]></text>
|
||||
</license>
|
||||
</licenses>
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
SetupWebPage::AddModule(
|
||||
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
|
||||
'itop-config/1.0.2',
|
||||
'itop-config/1.0.3',
|
||||
array(
|
||||
// Identification
|
||||
//
|
||||
@@ -49,5 +49,3 @@ SetupWebPage::AddModule(
|
||||
),
|
||||
)
|
||||
);
|
||||
|
||||
?>
|
||||
|
||||
@@ -17,8 +17,10 @@ Dict::Add('RU RU', 'Russian', 'Русский', array(
|
||||
'config-edit-intro' => 'Будьте очень осторожны при редактировании файла конфигурации. В частности, могут быть отредактированы только верхние элементы (т.е. глобальная конфигурация и настройки модулей).',
|
||||
'config-apply' => 'Применить',
|
||||
'config-cancel' => 'Сбросить',
|
||||
'config-saved' => 'Successfully recorded.~~',
|
||||
'config-confirm-cancel' => 'Ваши изменения будут утеряны.',
|
||||
'config-no-change' => 'Изменений нет: файл не был изменён.',
|
||||
'config-reverted' => 'The configuration has been reverted.~~',
|
||||
'config-parse-error' => 'Строка %2$d: %1$s.<br/>Файл не был обновлен.',
|
||||
'config-current-line' => 'Редактируемая строка: %1$s',
|
||||
));
|
||||
|
||||
Reference in New Issue
Block a user