From e07255952ad990a843ab5ecb22c4c38bdcd79cb1 Mon Sep 17 00:00:00 2001 From: Denis Flaven Date: Mon, 30 Mar 2009 10:57:35 +0000 Subject: [PATCH] - First version of the setup wizard (fully operational) SVN:code[17] --- setup/index.php | 241 +++++++++---- setup/menus.xml | 615 ++++++++++++++++++++++++++++++++++ setup/setup.js | 83 +++++ setup/setuppage.class.inc.php | 18 + 4 files changed, 901 insertions(+), 56 deletions(-) create mode 100644 setup/menus.xml create mode 100644 setup/setup.js diff --git a/setup/index.php b/setup/index.php index b301d5c9cc..ce83b1c7d9 100644 --- a/setup/index.php +++ b/setup/index.php @@ -13,15 +13,15 @@ define(MYSQL_MIN_VERSION, '5.0.0'); $sOperation = Utils::ReadParam('operation', 'step1'); $oP = new setup_web_page('iTop configuration wizard'); -$oP->no_cache(); /** * Helper function to check if the current version of PHP * is compatible with the application * @return boolean true if this is Ok, false otherwise */ -function CheckPHPVersion(nice_web_page $oP) +function CheckPHPVersion(setup_web_page $oP) { + $oP->log('Info - CheckPHPVersion'); if (version_compare(phpversion(), PHP_MIN_VERSION, '>=')) { $oP->ok("The current PHP Version (".phpversion().") is greater than the minimum required version (".PHP_MIN_VERSION.")"); @@ -31,13 +31,30 @@ function CheckPHPVersion(nice_web_page $oP) $oP->error("Error: The current PHP Version (".phpversion().") is lower than the minimum required version (".PHP_MIN_VERSION.")"); return false; } - if (extension_loaded('mysql')) + $aMandatoryExtensions = array('mysql', 'iconv', 'simplexml'); + asort($aMandatoryExtensions); // Sort the list to look clean ! + $aExtensionsOk = array(); + $aMissingExtensions = array(); + $aMissingExtensionsLinks = array(); + foreach($aMandatoryExtensions as $sExtension) { - $oP->ok("The required extension 'mysql' is present."); + if (extension_loaded($sExtension)) + { + $aExtensionsOk[] = $sExtension; + } + else + { + $aMissingExtensions[] = $sExtension; + $aMissingExtensionsLinks[] = "$sExtension"; + } } - else + if (count($aExtensionsOk) > 0) { - $oP->error("Error: missing required extension 'mysql'."); + $oP->ok("Required PHP extension(s): ".implode(', ', $aExtensionsOk)."."); + } + if (count($aMissingExtensions) > 0) + { + $oP->error("Missing PHP extension(s): ".implode(', ', $aMissingExtensionsLinks)."."); return false; } return true; @@ -48,9 +65,10 @@ function CheckPHPVersion(nice_web_page $oP) * the existing databases * @return Array The list of databases found in the server */ -function CheckServerConnection(nice_web_page $oP, $sDBServer, $sDBUser, $sDBPwd) +function CheckServerConnection(setup_web_page $oP, $sDBServer, $sDBUser, $sDBPwd) { $aResult = array(); + $oP->log('Info - CheckServerConnection'); try { $oDBSource = new CMDBSource; @@ -66,7 +84,15 @@ function CheckServerConnection(nice_web_page $oP, $sDBServer, $sDBUser, $sDBPwd) $oP->error("Error: Current MySQL version is ($sDBVersion), minimum required version (".MYSQL_MIN_VERSION.")"); return false; } - $aResult = $oDBSource->ListDB(); + try + { + $aResult = $oDBSource->ListDB(); + } + catch(Exception $e) + { + $oP->warning("Warning: unable to enumerate the current databases."); + $aResult = true; // Not an array to differentiate with an empty array + } } catch(Exception $e) { @@ -76,14 +102,16 @@ function CheckServerConnection(nice_web_page $oP, $sDBServer, $sDBUser, $sDBPwd) } return $aResult; } - + /** - * Helper function to create the database structure - * @return boolean true on success, false otherwise - */ -function CreateDatabaseStructure(nice_web_page $oP, Config $oConfig, $sDBName, $sDBPrefix) + * Helper function to initialize the ORM and load the data model + * from the given file + * @param $sConfigFileName string The name of the configuration file to load + * @param $bAllowMissingDatabase boolean Whether or not to allow loading a data model with no corresponding DB + * @return none + */ +function InitDataModel(setup_web_page $oP, $sConfigFileName, $bAllowMissingDatabase = true) { - $oP->info("Creating the structure in '$sDBName' (prefix = '$sDBPrefix')."); require_once('../core/coreexception.class.inc.php'); require_once('../core/attributedef.class.inc.php'); require_once('../core/filterdef.class.inc.php'); @@ -96,7 +124,18 @@ function CreateDatabaseStructure(nice_web_page $oP, Config $oConfig, $sDBName, $ require_once('../core/dbobjectsearch.class.php'); require_once('../core/dbobjectset.class.php'); require_once('../core/userrights.class.inc.php'); - MetaModel::Startup(TMP_CONFIG_FILE, true); // allow missing DB + $oP->log("Info - MetaModel::Startup from file '$sConfigFileName' (AllowMissingDB = $bAllowMissingDatabase)"); + MetaModel::Startup($sConfigFileName, $bAllowMissingDatabase); +} +/** + * Helper function to create the database structure + * @return boolean true on success, false otherwise + */ +function CreateDatabaseStructure(setup_web_page $oP, Config $oConfig, $sDBName, $sDBPrefix) +{ + InitDataModel($oP, TMP_CONFIG_FILE, true); // Allow the DB to NOT exist since we're about to create it ! + $oP->log('Info - CreateDatabaseStructure'); + $oP->info("Creating the structure in '$sDBName' (prefix = '$sDBPrefix')."); //MetaModel::CheckDefinitions(); if (!MetaModel::DBExists()) { @@ -106,6 +145,8 @@ function CreateDatabaseStructure(nice_web_page $oP, Config $oConfig, $sDBName, $ else { $oP->error("Error: database '$sDBName' (prefix = '$sDBPrefix') already exists."); + $oP->p("Tables with conflicting names already exist in the database. + Try selecting another database instance or specifiy a prefix to prevent conflicting table names."); return false; } return true; @@ -115,21 +156,10 @@ function CreateDatabaseStructure(nice_web_page $oP, Config $oConfig, $sDBName, $ * Helper function to create and administrator account for iTop * @return boolean true on success, false otherwise */ -function CreateAdminAccount(nice_web_page $oP, Config $oConfig, $sAdminUser, $sAdminPwd) +function CreateAdminAccount(setup_web_page $oP, Config $oConfig, $sAdminUser, $sAdminPwd) { - require_once('../core/coreexception.class.inc.php'); - require_once('../core/attributedef.class.inc.php'); - require_once('../core/filterdef.class.inc.php'); - require_once('../core/stimulus.class.inc.php'); - require_once('../core/MyHelpers.class.inc.php'); - require_once('../core/expression.class.inc.php'); - require_once('../core/cmdbsource.class.inc.php'); - require_once('../core/sqlquery.class.inc.php'); - require_once('../core/dbobject.class.php'); - require_once('../core/dbobjectsearch.class.php'); - require_once('../core/dbobjectset.class.php'); - require_once('../core/userrights.class.inc.php'); - MetaModel::Startup(TMP_CONFIG_FILE, true); // allow missing DB + $oP->log('Info - CreateAdminAccount'); + InitDataModel($oP, TMP_CONFIG_FILE, true); // allow missing DB if (UserRights::CreateAdministrator($sAdminUser, $sAdminPwd)) { $oP->ok("Administrator account '$sAdminUser' created."); @@ -143,35 +173,82 @@ function CreateAdminAccount(nice_web_page $oP, Config $oConfig, $sAdminUser, $sA } /** - * Helper function to load some sample data into the database + * Helper function to load the standard menus into the database */ -function LoadSampleData(nice_web_page $oP) +function LoadStandardMenus(setup_web_page $oP) { - // TO BE IMPLEMENTED + $oP->log('Info - LoadStandardMenus'); + + $oXml = simplexml_load_file('menus.xml'); + $aReplicas = array(); + foreach($oXml as $oXmlMenu) + { + $iPreviousId = (integer)$oXmlMenu['id']; // Mandatory to cast + $iParentId = (integer)$oXmlMenu->parent_id; // Mandatory to cast + // echo "

PreviousId = $iPreviousId; parent_id: $iParentId

\n"; + $oMenuNode = MetaModel::NewObject('menuNode'); + $oMenuNode->Set('name', $oXmlMenu->name); + $oMenuNode->Set('label', $oXmlMenu->label); + $oMenuNode->Set('hyperlink', $oXmlMenu->hyperlink); + $oMenuNode->Set('template', $oXmlMenu->template); + $oMenuNode->Set('rank', $oXmlMenu->rank); + $oMenuNode->DBInsert(); + $iDstId = $oMenuNode->GetKey(); + $aReplicas[$iPreviousId] = array('dstObj' => $oMenuNode, 'parentId' => $iParentId); + } + + foreach($aReplicas as $iKey => $aReplica) + { + $iSrcParentId = $aReplica['parentId']; + if ($iSrcParentId != 0) + { + if (isset($aReplicas[$iSrcParentId])) + { + $oParentMenu = $aReplicas[$iSrcParentId]['dstObj']; + $oMenu = $aReplica['dstObj']; + $oMenu->Set('parent_id', $oParentMenu->GetKey()); + $oMenu->DBUpdate(); + } + } + } + + $oP->ok("Standard menus have been created successfully."); + return true; +} + +/** + * Helper function to load sample data into the database + */ +function LoadSampleData(setup_web_page $oP) +{ + $oP->log('Info - LoadSampleData'); + $oP->ok("Sample data loaded into the database."); return true; } - + + /** * Display the form for the first step of the configuration wizard * which consists in the database server selection */ -function DisplayStep1(nice_web_page $oP) +function DisplayStep1(setup_web_page $oP) { $sNextOperation = 'step2'; $oP->add("

iTop configuration wizard

\n"); $oP->add("

Checking prerequisites

\n"); if (CheckPHPVersion($oP)) { + $sRedStar = '*'; $oP->add("

Step 1: Configuration of the database connection

\n"); - $oP->add("
\n"); + $oP->add("\n"); // Form goes here $oP->add("
Database connection\n"); $aForm = array(); - $aForm[] = array('label' => 'Server name:', 'input' => "", + $aForm[] = array('label' => "Server name$sRedStar:", 'input' => "", 'help' => 'E.g. "localhost", "dbserver.mycompany.com" or "192.142.10.23".'); - $aForm[] = array('label' => 'User name:', 'input' => ""); - $aForm[] = array('label' => 'Password:', 'input' => ""); + $aForm[] = array('label' => "User name$sRedStar:", 'input' => ""); + $aForm[] = array('label' => 'Password:', 'input' => ""); $oP->form($aForm); $oP->add("
\n"); $oP->add("\n"); @@ -186,12 +263,12 @@ function DisplayStep1(nice_web_page $oP) * 1) Validating the parameters by connecting to the database server * 2) Prompting to select an existing database or to create a new one */ -function DisplayStep2(nice_web_page $oP, Config $oConfig, $sDBServer, $sDBUser, $sDBPwd) +function DisplayStep2(setup_web_page $oP, Config $oConfig, $sDBServer, $sDBUser, $sDBPwd) { $sNextOperation = 'step3'; $oP->add("

iTop configuration wizard

\n"); $oP->add("

Step 2: Database selection

\n"); - $oP->add("\n"); + $oP->add("\n"); $aDatabases = CheckServerConnection($oP, $sDBServer, $sDBUser, $sDBPwd); if ($aDatabases === false) { @@ -206,18 +283,27 @@ function DisplayStep2(nice_web_page $oP, Config $oConfig, $sDBServer, $sDBUser, $oConfig->SetDBPwd($sDBPwd); $oConfig->WriteToFile(); - $oP->add("
Specify a database\n"); + $oP->add("
Specify a database*\n"); $aForm = array(); - foreach($aDatabases as $sDBName) + if (is_array($aDatabases)) { - $aForm[] = array('label' => " $sDBName"); + foreach($aDatabases as $sDBName) + { + $aForm[] = array('label' => ""); + } } - $aForm[] = array('label' => " create a new database: "); + else + { + $aForm[] = array('label' => ""); + $oP->add_ready_script("$('#current_db_name').click( function() { $('#current_db').attr('checked', true); });"); + } + $aForm[] = array('label' => ""); $oP->form($aForm); + $oP->add_ready_script("$('#new_db_name').click( function() { $('#new_db').attr('checked', true); });"); $oP->add("
\n"); $aForm = array(); - $aForm[] = array('label' => "Add a prefix to all the tables: "); + $aForm[] = array('label' => "Add a prefix to all the tables: "); $oP->form($aForm); $oP->add("\n"); @@ -235,24 +321,25 @@ function DisplayStep2(nice_web_page $oP, Config $oConfig, $sDBServer, $sDBUser, * 2) Creating the database structure * 3) Prompting for the admin account to be created */ -function DisplayStep3(nice_web_page $oP, Config $oConfig, $sDBName, $sDBPrefix) +function DisplayStep3(setup_web_page $oP, Config $oConfig, $sDBName, $sDBPrefix) { $sNextOperation = 'step4'; $oP->add("

iTop configuration wizard

\n"); $oP->add("

Creation of the database structure

\n"); - $oP->add("\n"); + $oP->add("\n"); $oConfig->SetDBName($sDBName); $oConfig->SetDBSubname($sDBPrefix); $oConfig->WriteToFile(TMP_CONFIG_FILE); if (CreateDatabaseStructure($oP, $oConfig, $sDBName, $sDBPrefix)) { + $sRedStar = "*"; $oP->add("

Step 3: Definition of the administrator account

\n"); // Database created, continue with admin creation $oP->add("
Administrator account\n"); $aForm = array(); - $aForm[] = array('label' => "Login:", 'input' => ""); - $aForm[] = array('label' => "Password:", 'input' => ""); - $aForm[] = array('label' => "Retype password:", 'input' => ""); + $aForm[] = array('label' => "Login$sRedStar:", 'input' => ""); + $aForm[] = array('label' => "Password$sRedStar:", 'input' => ""); + $aForm[] = array('label' => "Retype password$sRedStar:", 'input' => ""); $oP->form($aForm); $oP->add("
\n"); $oP->add("\n"); @@ -274,13 +361,13 @@ function DisplayStep3(nice_web_page $oP, Config $oConfig, $sDBName, $sDBPrefix) * 1) Creating the admin user account * 2) Prompting to load some sample data */ -function DisplayStep4(nice_web_page $oP, Config $oConfig, $sAdminUser, $sAdminPwd) +function DisplayStep4(setup_web_page $oP, Config $oConfig, $sAdminUser, $sAdminPwd) { $sNextOperation = 'step5'; $oP->add("

iTop configuration wizard

\n"); $oP->add("

Creation of the administrator account

\n"); - $oP->add("\n"); + $oP->add("\n"); if (CreateAdminAccount($oP, $oConfig, $sAdminUser, $sAdminPwd)) { $oP->add("

Step 4: Loading of sample data

\n"); @@ -309,7 +396,7 @@ function DisplayStep4(nice_web_page $oP, Config $oConfig, $sAdminUser, $sAdminPw * 1) Creating the final configuration file * 2) Prompting the user to make the file read-only */ -function DisplayStep5(nice_web_page $oP, Config $oConfig, $sAuthUser, $sAuthPwd, $bLoadSampleData) +function DisplayStep5(setup_web_page $oP, Config $oConfig, $sAuthUser, $sAuthPwd, $bLoadSampleData) { try { @@ -319,8 +406,7 @@ function DisplayStep5(nice_web_page $oP, Config $oConfig, $sAuthUser, $sAuthPwd, $oConfig->WriteToFile(FINAL_CONFIG_FILE); // Start the application - require_once('../application/application.inc.php'); - require_once('../application/startup.inc.php'); + InitDataModel($oP, FINAL_CONFIG_FILE, false); // DO NOT allow missing DB if (UserRights::Login($sAuthUser, $sAuthPwd)) { $_SESSION['auth_user'] = $sAuthUser; @@ -328,11 +414,12 @@ function DisplayStep5(nice_web_page $oP, Config $oConfig, $sAuthUser, $sAuthPwd, // remove the tmp config file @unlink(TMP_CONFIG_FILE); // try to make the final config file read-only - @chmod(FINAL_CONFIG_FILE, "a-w"); + @chmod(FINAL_CONFIG_FILE, 0440); // Read-only for owner and group, nothing for others $oP->add("

iTop configuration wizard

\n"); $oP->add("

Configuration completed

\n"); $oP->add("\n"); + LoadStandardMenus($oP); if ($bLoadSampleData) { LoadSampleData($oP); @@ -367,6 +454,39 @@ function DisplayStep5(nice_web_page $oP, Config $oConfig, $sAuthUser, $sAuthPwd, /** * Main program */ + clearstatcache(); // Make sure we know what we are doing ! +if (file_exists(FINAL_CONFIG_FILE)) +{ + // The configuration file already exists + if (is_writable(FINAL_CONFIG_FILE)) + { + $oP->warning("Warning: a configuration file '".FINAL_CONFIG_FILE."' already exists, and will be overwritten."); + } + else + { + $oP->add("

iTop configuration wizard

\n"); + $oP->add("

Fatal error

\n"); + $oP->error("Error: the configuration file '".FINAL_CONFIG_FILE."' already exists and cannot be overwritten."); + $oP->p("The wizard cannot create the configuration file for you. Please remove the file '".realpath(FINAL_CONFIG_FILE)."' or change its access-rights/read-only flag before continuing."); + $oP->output(); + exit; + } +} +else +{ + // No configuration file yet + // Check that the wizard can write into the root dir to create the configuration file + if (!is_writable(dirname(FINAL_CONFIG_FILE))) + { + $oP->add("

iTop configuration wizard

\n"); + $oP->add("

Fatal error

\n"); + $oP->error("Error: the directory where to store the configuration file is not writable."); + $oP->p("The wizard cannot create the configuration file for you. Please make sure that the directory '".realpath(dirname(FINAL_CONFIG_FILE))."' is writable for the web server."); + $oP->output(); + exit; + } + +} try { $oConfig = new Config(TMP_CONFIG_FILE); @@ -379,10 +499,13 @@ catch(Exception $e) switch($sOperation) { case 'step1': + $oP->log("Info - ========= Wizard step 1 ========"); DisplayStep1($oP); break; case 'step2': + $oP->no_cache(); + $oP->log("Info - ========= Wizard step 2 ========"); $sDBServer = Utils::ReadParam('db_server'); $sDBUser = Utils::ReadParam('db_user'); $sDBPwd = Utils::ReadParam('db_pwd'); @@ -390,6 +513,8 @@ switch($sOperation) break; case 'step3': + $oP->no_cache(); + $oP->log("Info - ========= Wizard step 3 ========"); $sDBName = Utils::ReadParam('db_name'); if (empty($sDBName)) { @@ -400,12 +525,16 @@ switch($sOperation) break; case 'step4': + $oP->no_cache(); + $oP->log("Info - ========= Wizard step 4 ========"); $sAdminUser = Utils::ReadParam('auth_user'); $sAdminPwd = Utils::ReadParam('auth_pwd'); DisplayStep4($oP, $oConfig, $sAdminUser, $sAdminPwd); break; case 'step5': + $oP->no_cache(); + $oP->log("Info - ========= Wizard step 5 ========"); $bLoadSampleData = (Utils::ReadParam('sample_data', 'no') == 'yes'); $sAdminUser = Utils::ReadParam('auth_user'); $sAdminPwd = Utils::ReadParam('auth_pwd'); diff --git a/setup/menus.xml b/setup/menus.xml new file mode 100644 index 0000000000..c3cb970fff --- /dev/null +++ b/setup/menus.xml @@ -0,0 +1,615 @@ + + + +0 +Admin Tools + +UI.php + +7 +application + + +5 +All Applications + +UI.php + +999 +application + + +5 +All Circuits + +UI.php + +999 +application + + +64 +All Contracts + +./UI.php + +2 +application + + +5 +All Interfaces + +UI.php + +999 +application + + +5 +All Network devices + +UI.php + +999 +application + + +5 +All Patches + +UI.php + +999 +application + + +5 +All PCs + +UI.php + +999 +application + + +5 +All Servers + +UI.php + +999 +application + + +1 +Audit + +/pages/audit.php + +4 +application + + +17 +Backup & Restore + +./db_importer.php + +998 +application + + +0 +Change Management + +./UI.php + +4 +application + + +66 +Closed Changes + +UI.php + +2 +application + + +61 +Closed Incident + +./UI.php + +2 +application + + +1 +Configuration Items + +UI.php + +2 +application + + +0 +Configuration Management + +UI.php + +2 +application + + +1 +Contacts + +UI.php + +1 +application + + +17 +CSV import + +csvimport.php + +998 +application + + +1 +Document + +UI.php + +6 +application + + +17 +Export + +./export.php + +1000 +application + + +1 +Grouping + +UI.php + +3 +application + + +0 +Incident Management + +./UI.php + +3 +application + + +61 +Known Errors + +./UI.php + +999 +application + + +1 +Locations + +UI.php + +5 +application + + +0 +My Bookmarks + +UI.php + +7 +user + + +57 +My Cisco Devices + +UI.php + +1 +user + + +57 +My Client Server + +UI.php + +1 +user + + +64 +Negociating contracts + +UI.php + +1 +application + + +17 +Objects Schema + +schema.php + +999 +application + + +66 +Open Changes + +./UI.php + +1 +application + + +61 +Open Incidents + +UI.php + +1 +application + + +2 +Persons + +UI.php + +7 +application + + +17 +Run queries + +./sibusql.php + +1001 +application + + +66 +Scheduled Outages + +./UI.php + +999 +application + + +0 +Service Management + +./UI.php + +5 +application + + +2 +Teams + +UI.php + +8 +application + + +17 +Universal Search + +UniversalSearch.php + +999 +application + + \ No newline at end of file diff --git a/setup/setup.js b/setup/setup.js new file mode 100644 index 0000000000..90ccb9f7ec --- /dev/null +++ b/setup/setup.js @@ -0,0 +1,83 @@ +function NameIsValid(name) +{ + sName = new String(name); + if (sName.match(/^[A-Za-z][A-Za-z0-9_]*$/)) return true; + return false; +} +function DoSubmit(sMsg, iStep) +{ + var bResult = true; + switch(iStep) + { + case 1: + if ($('#db_server').val() == '') + { + alert('Please specify a database server. Use "localhost" for a local DB server.'); + bResult = false; + } + else if ($('#db_user').val() == '') + { + alert('Please specify a user name to connect to the database.'); + bResult = false; + } + break; + + case 2: + if ($("input[@type=radio]:checked").length < 1) + { + alert('Please specify a database name'); + bResult = false; + } + else if( ($("#new_db:checked").length == 1)) + { + if ($('#new_db_name').val() == '') + { + alert('Please specify the name of the database to create'); + bResult = false; + } + else if (!NameIsValid($('#new_db_name').val())) + { + alert($('#new_db_name').val()+' is not a valid database name. Please limit yourself to letters, numbers and the underscore character.'); + bResult = false; + } + } + else if ($("#current_db:checked").length == 1) + { + // Special case (DB enumeration failed, user must enter DB name) + if ($("#current_db_name").val() == '') + { + alert('Please specify the name of the database.'); + bResult = false; + } + else + { + // Copy the typed value as the value of the radio + $("#current_db").val($("#current_db_name").val()); + } + } + if( ($('#db_prefix').val() != '') && (!NameIsValid($('#db_prefix').val())) ) + { + alert($('#db_prefix').val()+' is not a valid table name. Please limit yourself to letters, numbers and the underscore character.'); + bResult = false; + } + break; + + case 3: + if ($('#auth_user').val() == '') + { + alert('Please specify a login name for the administrator account'); + bResult = false; + } + else if ($('#auth_pwd').val() != $('#auth_pwd2').val()) + { + alert('Retyped password does not match! Please verify the password.'); + bResult = false; + } + break; + } + if (bResult) + { + $('#setup').block({message: ' '+sMsg}); + } + return bResult; +} diff --git a/setup/setuppage.class.inc.php b/setup/setuppage.class.inc.php index 28579298a8..64039013ee 100644 --- a/setup/setuppage.class.inc.php +++ b/setup/setuppage.class.inc.php @@ -1,5 +1,6 @@ add_linked_script("../js/jquery.blockUI.js"); + $this->add_linked_script("./setup.js"); $this->add_style(" body { background-color: #eee; @@ -92,21 +95,25 @@ table.formTable { public function info($sText) { $this->add("

$sText

\n"); + $this->log("Info - ".$sText); } public function ok($sText) { $this->add("

$sText

\n"); + $this->log("Ok - ".$sText); } public function warning($sText) { $this->add("

$sText

\n"); + $this->log("Warning - ".$sText); } public function error($sText) { $this->add("

$sText

\n"); + $this->log("Error - ".$sText); } public function form($aData) @@ -145,5 +152,16 @@ table.formTable { $this->s_content = "
{$this->s_content}\n
\n"; return parent::output(); } + + public function log($sText) + { + $hLogFile = @fopen(INSTALL_LOG_FILE, 'a'); + if ($hLogFile !== false) + { + $sDate = date('Y-m-d H:i:s'); + fwrite($hLogFile, "$sDate - $sText\n"); + fclose($hLogFile); + } + } } // End of class ?>