浏览代码

New functionality: a rudimentary configuration editor (admins only)

git-svn-id: http://svn.code.sf.net/p/itop/code/trunk@3401 a333f486-631f-4898-b8df-5754b55c2be0
romainq 10 年之前
父节点
当前提交
dfe8176da5

+ 217 - 0
datamodels/2.x/itop-config/config.php

@@ -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();
+?>

+ 20 - 0
datamodels/2.x/itop-config/de.dict.itop-config.php

@@ -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',
+));

+ 21 - 0
datamodels/2.x/itop-config/en.dict.itop-config.php

@@ -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',
+));
+?>

+ 21 - 0
datamodels/2.x/itop-config/fr.dict.itop-config.php

@@ -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',
+));
+?>

+ 30 - 0
datamodels/2.x/itop-config/main.itop-config.php

@@ -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 */);
+		}
+	}
+}

+ 53 - 0
datamodels/2.x/itop-config/module.itop-config.php

@@ -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(
+		),
+	)
+);
+
+?>