New functionality: a rudimentary configuration editor (admins only)

SVN:trunk[3401]
This commit is contained in:
Romain Quetiez
2014-11-03 09:17:27 +00:00
parent 96f02d1557
commit 2c70c60d2b
6 changed files with 362 additions and 0 deletions

View File

@@ -0,0 +1,217 @@
<?php
// Copyright (C) 2013 Combodo SARL
//
// This file is part of iTop.
//
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// iTop is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
/**
* Monitor the backup
*
* @copyright Copyright (C) 2013 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
require_once('../../approot.inc.php');
require_once(APPROOT.'application/application.inc.php');
require_once(APPROOT.'application/itopwebpage.class.inc.php');
require_once(APPROOT.'application/startup.inc.php');
require_once(APPROOT.'application/loginwebpage.class.inc.php');
function TestConfig($sContents, $oP)
{
try
{
ini_set('display_errors', 1);
ob_start();
eval('?'.'>'.trim($sContents));
$sNoise = trim(ob_get_contents());
ob_end_clean();
}
catch (Exception $e)
{
// well, never reach in case of parsing error :-(
throw new Exception('Error in configuration: '.$e->getMessage());
}
if (strlen($sNoise) > 0)
{
if (preg_match("/(Error|Parse error|Notice|Warning): (.+) in \S+ : eval\(\)'d code on line (\d+)/i", strip_tags($sNoise), $aMatches))
{
$sMessage = $aMatches[2];
$sLine = $aMatches[3];
$iLine = (int) $sLine;
// Highlight the line
$aLines = explode("\n", $sContents);
$iStart = 0;
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);
}
else
{
// Note: sNoise is an html output, but so far it was ok for me (e.g. showing the entire call stack)
throw new Exception('Syntax error in configuration file: <tt>'.$sNoise.'</tt>');
}
}
}
/////////////////////////////////////////////////////////////////////
// Main program
//
LoginWebPage::DoLogin(true); // Check user rights and prompt if needed (must be admin)
//$sOperation = utils::ReadParam('operation', 'menu');
//$oAppContext = new ApplicationContext();
$oP = new iTopWebPage(Dict::S('config-edit-title'));
$oP->set_base(utils::GetAbsoluteUrlAppRoot().'pages/');
try
{
$sOperation = utils::ReadParam('operation', '');
$oP->add("<h1>".Dict::S('config-edit-title')."</h1>");
$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');
$sOrginalConfig = utils::ReadParam('prev_config', '', false, 'raw_data');
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
file_put_contents($sConfigFile, $sConfig);
@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;
}
$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 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\" 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("</form>");
$sConfirmCancel = addslashes(Dict::S('config-confirm-cancel'));
$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())
{
if (confirm('$sConfirmCancel'))
{
$('#new_config').val($('#prev_config').val());
}
}
$('.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
);
}
catch(Exception $e)
{
$oP->p('<b>'.$e->getMessage().'</b>');
}
$oP->output();
?>

View File

@@ -0,0 +1,20 @@
<?php
/**
* Localized data
*
* @copyright Copyright (C) 2013 Combodo
* @license http://opensource.org/licenses/AGPL-3.0
*/
Dict::Add('DE DE', 'German', 'Deutsch', array(
'Menu:ConfigEditor' => 'Konfiguration',
'config-edit-title' => 'Konfigurations-Editor',
'config-edit-intro' => 'Seien sie bei der Bearbeitung der Konfigurationsdatei sehr vorsichtig. Normalerweise sollten es aureichen die Einträge im oberen Teil der Konfiguration zu bearbeiten (Bsp.: die globalen Konfigurations- und Moduleinstellungen).',
'config-apply' => 'Anwenden',
'config-cancel' => 'Zurücksetzen',
'config-confirm-cancel' => 'Ihre Änderungen werden nicht gespeichert.',
'config-no-change' => 'Keine Änderungen: Die Datei wurde nicht verändert.',
'config-parse-error' => 'Zeile %2$d: %1$s.<br/>Die Datei wurde nicht aktualisiert.',
'config-current-line' => 'Editiere Zeile: %1$s',
));

View File

@@ -0,0 +1,21 @@
<?php
/**
* Localized data
*
* @copyright Copyright (C) 2013 Combodo
* @license http://opensource.org/licenses/AGPL-3.0
*/
Dict::Add('EN US', 'English', 'English', array(
'Menu:ConfigEditor' => 'Configuration',
'config-edit-title' => 'Configuration File Editor',
'config-edit-intro' => 'Be very cautious when editing the configuration file. In particular, only the upper items (i.e. the global configuration and modules settings) should be edited.',
'config-apply' => 'Apply',
'config-cancel' => 'Reset',
'config-confirm-cancel' => 'Your changes will be lost.',
'config-no-change' => 'No change: the file has been left unchanged.',
'config-parse-error' => 'Line %2$d: %1$s.<br/>The file has NOT been updated.',
'config-current-line' => 'Editing line: %1$s',
));
?>

View File

@@ -0,0 +1,21 @@
<?php
/**
* Localized data
*
* @copyright Copyright (C) 2013 Combodo
* @license http://opensource.org/licenses/AGPL-3.0
*/
Dict::Add('FR FR', 'French', 'Français', array(
'Menu:ConfigEditor' => 'Configuration',
'config-edit-title' => 'Editeur du Fichier de Configuration',
'config-edit-intro' => 'Attention: une configuration incorrecte peut rendre iTop indisponible. En particulier, vous ne devriez éditer QUE les deux premiers éléments, à savoir la configuration globale et la configuration des modules.',
'config-save' => 'Appliquer',
'config-restore' => 'Réinitialiser',
'config-confirm-cancel' => 'Vos modifications seront perdues.',
'config-no-change' => 'Aucun changement : le fichier n\'a pas été altéré.',
'config-parse-error' => 'Ligne %2$d: %1$s.<br/>Le fichier n\'a PAS été modifié.',
'config-current-line' => 'Ligne en édition : %1$s',
));
?>

View File

@@ -0,0 +1,30 @@
<?php
// Copyright (C) 2013 Combodo SARL
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; version 3 of the License.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//require_once(APPROOT.'setup/setuputils.class.inc.php');
class ItopConfigEditor extends ModuleHandlerAPI
{
public static function OnMenuCreation()
{
if (UserRights::IsAdministrator())
{
$oAdminMenu = new MenuGroup('AdminTools', 80 /* fRank */);
new WebPageMenuNode('ConfigEditor', utils::GetAbsoluteUrlModulesRoot().'itop-config/config.php', $oAdminMenu->GetIndex(), 18 /* fRank */);
}
}
}

View File

@@ -0,0 +1,53 @@
<?php
SetupWebPage::AddModule(
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
'itop-config/1.0.1',
array(
// Identification
//
'label' => 'Configuration editor',
'category' => 'Application management',
// Setup
//
'dependencies' => array(
),
'mandatory' => true,
'visible' => false,
// Components
//
'datamodel' => array(
'main.itop-config.php',
//'model.itop-config.php',
),
'webservice' => array(
//'webservices.itop-config.php',
),
'dictionary' => array(
'en.dict.itop-config.php',
'fr.dict.itop-config.php',
//'de.dict.itop-config.php',
),
'data.struct' => array(
//'data.struct.itop-config.xml',
),
'data.sample' => array(
//'data.sample.itop-config.xml',
),
// Documentation
//
'doc.manual_setup' => '',
'doc.more_information' => '',
// Default settings
//
'settings' => array(
),
)
);
?>