diff --git a/core/metamodel.class.php b/core/metamodel.class.php
index a450d976c..9ff9b836c 100644
--- a/core/metamodel.class.php
+++ b/core/metamodel.class.php
@@ -3549,9 +3549,9 @@ abstract class MetaModel
}
}
- public static function Startup($sConfigFile, $bModelOnly = false)
+ public static function Startup($sConfigFile, $bModelOnly = false, $bUseCache = true)
{
- self::LoadConfig($sConfigFile);
+ self::LoadConfig($sConfigFile, $bUseCache);
if ($bModelOnly) return;
@@ -3573,10 +3573,10 @@ abstract class MetaModel
}
}
- public static function LoadConfig($sConfigFile)
+ public static function LoadConfig($sConfigFile, $bUseCache = false)
{
self::$m_oConfig = new Config($sConfigFile);
-
+
// Set log ASAP
if (self::$m_oConfig->GetLogGlobal())
{
@@ -3655,7 +3655,7 @@ abstract class MetaModel
$sCharacterSet = self::$m_oConfig->GetDBCharacterSet();
$sCollation = self::$m_oConfig->GetDBCollation();
- if (function_exists('apc_fetch'))
+ if ($bUseCache && function_exists('apc_fetch'))
{
$oKPI = new ExecutionKPI();
// Note: For versions of APC older than 3.0.17, fetch() accepts only one parameter
@@ -3698,7 +3698,7 @@ abstract class MetaModel
self::InitClasses($sTablePrefix);
$oKPI->ComputeAndReport('Initialization of Data model structures');
- if (function_exists('apc_store'))
+ if ($bUseCache && function_exists('apc_store'))
{
$oKPI = new ExecutionKPI();
@@ -4123,12 +4123,11 @@ abstract class MetaModel
}
}
- public static function GetCacheEntries()
+ public static function GetCacheEntries($sAppIdentity)
{
if (!function_exists('apc_cache_info')) return array();
$aCacheUserData = apc_cache_info('user');
- $sAppIdentity = MetaModel::GetConfig()->Get('session_name');
$sPrefix = $sAppIdentity.'-';
$aEntries = array();
@@ -4144,14 +4143,14 @@ abstract class MetaModel
return $aEntries;
}
- public static function ResetCache()
+ public static function ResetCache(Config $oConfig)
{
if (!function_exists('apc_delete')) return;
- $sAppIdentity = MetaModel::GetConfig()->Get('session_name');
+ $sAppIdentity = $oConfig->Get('session_name');
Dict::ResetCache($sAppIdentity);
- foreach(self::GetCacheEntries() as $sKey => $aAPCInfo)
+ foreach(self::GetCacheEntries($sAppIdentity) as $sKey => $aAPCInfo)
{
$sAPCKey = $aAPCInfo['info'];
apc_delete($sAPCKey);
diff --git a/modules/itop-profiles-itil/module.itop-profiles-itil.php b/modules/itop-profiles-itil/module.itop-profiles-itil.php
index 58ae6b098..37ad030ff 100644
--- a/modules/itop-profiles-itil/module.itop-profiles-itil.php
+++ b/modules/itop-profiles-itil/module.itop-profiles-itil.php
@@ -75,11 +75,12 @@ class CreateITILProfilesInstaller extends ModuleInstallerAPI
return $oConfiguration;
}
- public static function AfterDatabaseCreation(Config $oConfiguration)
+ public static function AfterDatabaseCreation(Config $oConfiguration, $sPreviousVersion, $sCurrentVersion)
{
self::ComputeITILProfiles();
//self::ComputeBasicProfiles();
- self::DoCreateProfiles();
+ $bFirstInstall = empty($sPreviousVersion);
+ self::DoCreateProfiles($bFirstInstall);
UserRights::FlushPrivileges(true /* reset admin cache */);
}
@@ -298,10 +299,14 @@ class CreateITILProfilesInstaller extends ModuleInstallerAPI
DBObject::BulkInsertFlush();
}
- public static function DoCreateProfiles()
+ public static function DoCreateProfiles($bFirstInstall = true)
{
- URP_Profiles::DoCreateAdminProfile();
- URP_Profiles::DoCreateUserPortalProfile();
+ if ($bFirstInstall)
+ {
+ // Makae sure we create these special profiles only once
+ URP_Profiles::DoCreateAdminProfile();
+ URP_Profiles::DoCreateUserPortalProfile();
+ }
foreach(self::$m_aProfiles as $sName => $aProfileData)
{
diff --git a/setup/ajax.dataloader.php b/setup/ajax.dataloader.php
index 7bacd750f..886e8f301 100644
--- a/setup/ajax.dataloader.php
+++ b/setup/ajax.dataloader.php
@@ -24,16 +24,26 @@
*/
/**
- * This page is called to load "asynchronously" some xml file into the database
+ * This page is called to perform "asynchronously" the setup actions
* parameters
- * 'file' string Name of the file to load
- * 'session_status' string 'start', 'continue' or 'end'
- * 'percent' integer 0..100 the percentage of completion once the file has been loaded
+ * 'operation': one of 'update_db_schema', 'after_db_creation', 'file'
+ *
+ * if 'operation' == 'update_db_schema':
+ * 'mode': install | upgrade
+ *
+ * if 'operation' == 'after_db_creation':
+ * 'mode': install | upgrade
+ *
+ * if 'operation' == 'file':
+ * 'file': string Name of the file to load
+ * 'session_status': string 'start', 'continue' or 'end'
+ * 'percent': integer 0..100 the percentage of completion once the file has been loaded
*/
define('SAFE_MINIMUM_MEMORY', 32*1024*1024);
require_once('../approot.inc.php');
require_once(APPROOT.'/application/utils.inc.php');
require_once(APPROOT.'/setup/setuppage.class.inc.php');
+require_once(APPROOT.'/setup/moduleinstaller.class.inc.php');
$sMemoryLimit = trim(ini_get('memory_limit'));
if (empty($sMemoryLimit))
@@ -78,7 +88,24 @@ function FatalErrorCatcher($sOutput)
}
return $sOutput;
}
-
+
+/**
+ * Helper function to create and administrator account for iTop
+ * @return boolean true on success, false otherwise
+ */
+function CreateAdminAccount(Config $oConfig, $sAdminUser, $sAdminPwd, $sLanguage)
+{
+ SetupWebPage::log_info('CreateAdminAccount');
+
+ if (UserRights::CreateAdministrator($sAdminUser, $sAdminPwd, $sLanguage))
+ {
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
//Define some bogus, invalid HTML tags that no sane
//person would ever put in an actual document and tell
//PHP to delimit fatal error warnings with them.
@@ -104,50 +131,128 @@ header("Expires: Fri, 17 Jul 1970 05:00:00 GMT"); // Date in the past
/**
* Main program
*/
-$sFileName = Utils::ReadParam('file', '');
-$sSessionStatus = Utils::ReadParam('session_status', '');
-$iPercent = (integer)Utils::ReadParam('percent', 0);
-SetupWebPage::log_info("Loading file: $sFileName");
-
+$sOperation = Utils::ReadParam('operation', '');
try
{
- if (empty($sFileName) || !file_exists($sFileName))
+ switch($sOperation)
{
- throw(new Exception("File $sFileName does not exist"));
- }
+
+ case 'update_db_schema':
+ SetupWebPage::log_info("Update Database Schema.");
+ InitDataModel(TMP_CONFIG_FILE, true); // load data model and connect to the database
+ $sMode = Utils::ReadParam('mode', 'install');
+ $sSelectedModules = Utils::ReadParam('selected_modules', '');
+ $aSelectedModules = explode(',', $sSelectedModules);
+ if(!CreateDatabaseStructure(MetaModel::GetConfig(), $aSelectedModules, $sMode))
+ {
+ throw(new Exception("Failed to create/upgrade the database structure"));
+ }
+ SetupWebPage::log_info("Database Schema Successfully Updated.");
+ break;
+
+ case 'after_db_create':
+ SetupWebPage::log_info('After Database Creation');
+ $sMode = Utils::ReadParam('mode', 'install');
+ $sSelectedModules = Utils::ReadParam('selected_modules', '');
+ $aSelectedModules = explode(',', $sSelectedModules);
+ InitDataModel(TMP_CONFIG_FILE, true); // load data model and connect to the database
+
+ // Perform here additional DB setup... profiles, etc...
+
+ $aAvailableModules = AnalyzeInstallation(MetaModel::GetConfig());
- $oDataLoader = new XMLDataLoader(TMP_CONFIG_FILE); // When called by the wizard, the final config is not yet there
- if ($sSessionStatus == 'start')
- {
- $oChange = MetaModel::NewObject("CMDBChange");
- $oChange->Set("date", time());
- $oChange->Set("userinfo", "Initialization");
- $iChangeId = $oChange->DBInsert();
- SetupWebPage::log_info("starting data load session");
- $oDataLoader->StartSession($oChange);
- }
+ $aStructureDataFiles = array();
+ $aSampleDataFiles = array();
+
+ foreach($aAvailableModules as $sModuleId => $aModule)
+ {
+ if (($sModuleId != 'iTop') && in_array($sModuleId, $aSelectedModules) &&
+ isset($aAvailableModules[$sModuleId]['installer']) )
+ {
+ $sModuleInstallerClass = $aAvailableModules[$sModuleId]['installer'];
+ SetupWebPage::log_info("Calling Module Handler: $sModuleInstallerClass::AfterDatabaseCreation(oConfig, {$aModule['version_db']}, {$aModule['version_code']})");
+ // The validity of the sModuleInstallerClass has been established in BuildConfig()
+ $aCallSpec = array($sModuleInstallerClass, 'AfterDatabaseCreation');
+ call_user_func_array($aCallSpec, array(MetaModel::GetConfig(), $aModule['version_db'], $aModule['version_code']));
+ }
+ }
- $oDataLoader->LoadFile($sFileName);
- $sResult = sprintf("loading of %s done. (Overall %d %% completed).", basename($sFileName), $iPercent);
- //echo $sResult;
- SetupWebPage::log_info($sResult);
- if ($sSessionStatus == 'end')
- {
- $oDataLoader->EndSession();
- SetupWebPage::log_info("ending data load session");
+ if (!RecordInstallation(MetaModel::GetConfig(), $aSelectedModules))
+ {
+ throw(new Exception("Failed to record the installation information"));
+ }
+
+ if($sMode == 'install')
+ {
+ // Create the admin user only in case of installation
+ $sAdminUser = Utils::ReadParam('auth_user', '');
+ $sAdminPwd = Utils::ReadParam('auth_pwd', '');
+ $sLanguage = Utils::ReadParam('language', '');
+ if (!CreateAdminAccount(MetaModel::GetConfig(), $sAdminUser, $sAdminPwd, $sLanguage))
+ {
+ throw(new Exception("Failed to create the administrator account '$sAdminUser'"));
+ }
+ else
+ {
+ SetupWebPage::log_info("Administrator account '$sAdminUser' created.");
+ }
+ }
+ break;
+
+ case 'load_data': // Load data files
+ $sFileName = Utils::ReadParam('file', '');
+ $sSessionStatus = Utils::ReadParam('session_status', '');
+ $iPercent = (integer)Utils::ReadParam('percent', 0);
+ SetupWebPage::log_info("Loading file: $sFileName");
+ if (empty($sFileName) || !file_exists($sFileName))
+ {
+ throw(new Exception("File $sFileName does not exist"));
+ }
+
+ $oDataLoader = new XMLDataLoader(TMP_CONFIG_FILE); // When called by the wizard, the final config is not yet there
+ if ($sSessionStatus == 'start')
+ {
+ $oChange = MetaModel::NewObject("CMDBChange");
+ $oChange->Set("date", time());
+ $oChange->Set("userinfo", "Initialization");
+ $iChangeId = $oChange->DBInsert();
+ SetupWebPage::log_info("starting data load session");
+ $oDataLoader->StartSession($oChange);
+ }
+
+ $oDataLoader->LoadFile($sFileName);
+ $sResult = sprintf("loading of %s done. (Overall %d %% completed).", basename($sFileName), $iPercent);
+ SetupWebPage::log_info($sResult);
+
+ if ($sSessionStatus == 'end')
+ {
+ $oDataLoader->EndSession();
+ SetupWebPage::log_info("ending data load session");
+ }
+ break;
+
+ default:
+ throw(new Exception("Error unsupported operation '$sOperation'"));
}
}
catch(Exception $e)
{
header("HTTP/1.0 500 Internal server error.");
- echo "
An error happened while loading the data
\n";
+ echo "An error happened while processing the installation:
\n";
echo ''.$e."
\n";
- SetupWebPage::log_error("An error happened while loading the data. ".$e);
+ SetupWebPage::log_error("An error happened while processing the installation: ".$e);
}
if (function_exists('memory_get_peak_usage'))
{
- SetupWebPage::log_info("loading file '$sFileName', peak memory usage. ".memory_get_peak_usage());
+ if ($sOperation == 'file')
+ {
+ SetupWebPage::log_info("loading file '$sFileName', peak memory usage. ".memory_get_peak_usage());
+ }
+ else
+ {
+ SetupWebPage::log_info("operation '$sOperation', peak memory usage. ".memory_get_peak_usage());
+ }
}
?>
diff --git a/setup/index.php b/setup/index.php
index d99a8769e..ad6f492f1 100644
--- a/setup/index.php
+++ b/setup/index.php
@@ -1,1326 +1,1517 @@
-
- * @author Romain Quetiez
- * @author Denis Flaven
- * @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
- */
-
-require_once('../approot.inc.php');
-require_once(APPROOT.'/application/utils.inc.php');
-require_once(APPROOT.'/core/config.class.inc.php');
-require_once(APPROOT.'/core/log.class.inc.php');
-require_once(APPROOT.'/core/kpi.class.inc.php');
-require_once(APPROOT.'/core/cmdbsource.class.inc.php');
-require_once(APPROOT.'/setup/setuppage.class.inc.php');
-require_once(APPROOT.'/setup/moduleinstaller.class.inc.php');
-
-define('TMP_CONFIG_FILE', APPROOT.'/tmp-config-itop.php');
-define('FINAL_CONFIG_FILE', APPROOT.'/config-itop.php');
-define('PHP_MIN_VERSION', '5.2.0');
-define('MYSQL_MIN_VERSION', '5.0.0');
-define('MIN_MEMORY_LIMIT', 32*1024*1024);
-
-
-$sOperation = Utils::ReadParam('operation', 'step0');
-$oP = new SetupWebPage('iTop configuration wizard');
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// Various helper function
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-/**
- * Get a nicely formatted version string
- */
-function GetITopVersion($bShort = true)
-{
- $sVersionString = '';
- if ($bShort)
- {
- $sVersionString = "iTop Version ".ITOP_VERSION;
- }
- else
- {
- if (ITOP_REVISION == '$WCREV$')
- {
- // This is NOT a version built using the buil system, just display the main version
- $sVersionString = "iTop Version ".ITOP_VERSION;
- }
- else
- {
- // This is a build made from SVN, let display the full information
- $sVersionString = "iTop Version ".ITOP_VERSION." revision ".ITOP_REVISION.", built on: ".ITOP_BUILD_DATE;
- }
- }
- return $sVersionString;
-}
-
-/**
- * Helper function to retrieve the system's temporary directory
- * Emulates sys_get_temp_dir if neeed (PHP < 5.2.1)
- * @return string Path to the system's temp directory
- */
-function GetTmpDir()
-{
- // try to figure out what is the temporary directory
- // prior to PHP 5.2.1 the function sys_get_temp_dir
- // did not exist
- if ( !function_exists('sys_get_temp_dir'))
- {
- if( $temp=getenv('TMP') ) return realpath($temp);
- if( $temp=getenv('TEMP') ) return realpath($temp);
- if( $temp=getenv('TMPDIR') ) return realpath($temp);
- $temp=tempnam(__FILE__,'');
- if (file_exists($temp))
- {
- unlink($temp);
- return realpath(dirname($temp));
- }
- return null;
- }
- else
- {
- return realpath(sys_get_temp_dir());
- }
-}
-
-/**
- * Check the value of the PHP setting 'memory_limit'
- * against the minimum recommended value
- * @param SetpWebPage $oP The current web page
- * @param integer $iMinMemoryRequired The minimum memory for the test to pass
- * @return boolean Whether or not it's Ok to continue
- */
-function CheckMemoryLimit(SetupWebPage $oP, $iMinMemoryRequired)
-{
- $sMemoryLimit = trim(ini_get('memory_limit'));
- $bResult = true;
- if (empty($sMemoryLimit))
- {
- // On some PHP installations, memory_limit does not exist as a PHP setting!
- // (encountered on a 5.2.0 under Windows)
- // In that case, ini_set will not work, let's keep track of this and proceed anyway
- $oP->warning("No memory limit has been defined in this instance of PHP");
- }
- else
- {
- // Check that the limit will allow us to load the data
- //
- $iMemoryLimit = utils::ConvertToBytes($sMemoryLimit);
- if ($iMemoryLimit < $iMinMemoryRequired)
- {
- $oP->error("memory_limit ($iMemoryLimit) is too small, the minimum value to run iTop is $iMinMemoryRequired.");
- $bResult = false;
- }
- else
- {
- $oP->log_info("memory_limit is $iMemoryLimit, ok.");
- }
- }
- return $bResult;
-}
-/**
- * Helper function to retrieve the directory where files are to be uploaded
- * @return string Path to the temp directory used for uploading files
- */
-function GetUploadTmpDir()
-{
- $sPath = ini_get('upload_tmp_dir');
- if (empty($sPath))
- {
- $sPath = GetTmpDir();
- }
- return $sPath;
-}
-
-/**
- * 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(SetupWebPage $oP)
-{
- $bResult = true;
- $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.")");
- }
- else
- {
- $oP->error("Error: The current PHP Version (".phpversion().") is lower than the minimum required version (".PHP_MIN_VERSION.")");
- return false;
- }
- $aMandatoryExtensions = array('mysql', 'iconv', 'simplexml', 'soap', 'hash', 'json', 'session', 'pcre', 'dom');
- $aOptionalExtensions = array('mcrypt' => 'Strong encryption will not be used.',
- 'ldap' => 'LDAP authentication will be disabled.');
- asort($aMandatoryExtensions); // Sort the list to look clean !
- ksort($aOptionalExtensions); // Sort the list to look clean !
- $aExtensionsOk = array();
- $aMissingExtensions = array();
- $aMissingExtensionsLinks = array();
- // First check the mandatory extensions
- foreach($aMandatoryExtensions as $sExtension)
- {
- if (extension_loaded($sExtension))
- {
- $aExtensionsOk[] = $sExtension;
- }
- else
- {
- $aMissingExtensions[] = $sExtension;
- $aMissingExtensionsLinks[] = "$sExtension ";
- }
- }
- if (count($aExtensionsOk) > 0)
- {
- $oP->ok("Required PHP extension(s): ".implode(', ', $aExtensionsOk).".");
- }
- if (count($aMissingExtensions) > 0)
- {
- $oP->error("Missing PHP extension(s): ".implode(', ', $aMissingExtensionsLinks).".");
- $bResult = false;
- }
- // Next check the optional extensions
- $aExtensionsOk = array();
- $aMissingExtensions = array();
- foreach($aOptionalExtensions as $sExtension => $sMessage)
- {
- if (extension_loaded($sExtension))
- {
- $aExtensionsOk[] = $sExtension;
- }
- else
- {
- $aMissingExtensions[$sExtension] = $sMessage;
- }
- }
- if (count($aExtensionsOk) > 0)
- {
- $oP->ok("Optional PHP extension(s): ".implode(', ', $aExtensionsOk).".");
- }
- if (count($aMissingExtensions) > 0)
- {
- foreach($aMissingExtensions as $sExtension => $sMessage)
- {
- $oP->warning("Missing optional PHP extension: $sExtension. ".$sMessage);
- }
- }
- // Check some ini settings here
- if (function_exists('php_ini_loaded_file')) // PHP >= 5.2.4
- {
- $sPhpIniFile = php_ini_loaded_file();
- // Other included/scanned files
- if ($sFileList = php_ini_scanned_files())
- {
- if (strlen($sFileList) > 0)
- {
- $aFiles = explode(',', $sFileList);
-
- foreach ($aFiles as $sFile)
- {
- $sPhpIniFile .= ', '.trim($sFile);
- }
- }
- }
- $oP->log("Info - php.ini file(s): '$sPhpIniFile'");
- }
- else
- {
- $sPhpIniFile = 'php.ini';
- }
- if (!ini_get('file_uploads'))
- {
- $oP->error("Files upload is not allowed on this server (file_uploads = ".ini_get('file_uploads').").");
- $bResult = false;
- }
-
- $sUploadTmpDir = GetUploadTmpDir();
- if (empty($sUploadTmpDir))
- {
- $sUploadTmpDir = '/tmp';
- $oP->warning("Temporary directory for files upload is not defined (upload_tmp_dir), assuming that $sUploadTmpDir is used.");
- }
- // check that the upload directory is indeed writable from PHP
- if (!empty($sUploadTmpDir))
- {
- if (!file_exists($sUploadTmpDir))
- {
- $oP->error("Temporary directory for files upload ($sUploadTmpDir) does not exist or cannot be read by PHP.");
- $bResult = false;
- }
- else if (!is_writable($sUploadTmpDir))
- {
- $oP->error("Temporary directory for files upload ($sUploadTmpDir) is not writable.");
- $bResult = false;
- }
- else
- {
- $oP->log("Info - Temporary directory for files upload ($sUploadTmpDir) is writable.");
- }
- }
-
-
- if (!ini_get('upload_max_filesize'))
- {
- $oP->error("File upload is not allowed on this server (file_uploads = ".ini_get('file_uploads').").");
- }
-
- $iMaxFileUploads = ini_get('max_file_uploads');
- if (!empty($iMaxFileUploads) && ($iMaxFileUploads < 1))
- {
- $oP->error("File upload is not allowed on this server (max_file_uploads = ".ini_get('max_file_uploads').").");
- $bResult = false;
- }
- $oP->log("Info - upload_max_filesize: ".ini_get('upload_max_filesize'));
- $oP->log("Info - max_file_uploads: ".ini_get('max_file_uploads'));
-
- // Check some more ini settings here, needed for file upload
- if (get_magic_quotes_gpc())
- {
- $oP->error("'magic_quotes_gpc' is set to On. Please turn it Off before continuing. You may want to check the PHP configuration file(s): '$sPhpIniFile'. Be aware that this setting can also be overridden in the apache configuration.");
- $bResult = false;
- }
-
- $bResult = $bResult & CheckMemoryLimit($oP, MIN_MEMORY_LIMIT);
-
- return $bResult;
-}
-
-/**
- * Helper function check the connection to the database and (if connected) to enumerate
- * the existing databases
- * @return Array The list of databases found in the server
- */
-function CheckServerConnection(SetupWebPage $oP, $sDBServer, $sDBUser, $sDBPwd)
-{
- $aResult = array();
- $oP->log('Info - CheckServerConnection');
- try
- {
- $oDBSource = new CMDBSource;
- $oDBSource->Init($sDBServer, $sDBUser, $sDBPwd);
- $oP->ok("Connection to '$sDBServer' as '$sDBUser' successful.");
-
- $oP->log("Info - User privileges: ".($oDBSource->GetRawPrivileges()));
-
- $sDBVersion = $oDBSource->GetDBVersion();
- if (version_compare($sDBVersion, MYSQL_MIN_VERSION, '>='))
- {
- $oP->ok("Current MySQL version ($sDBVersion), greater than minimum required version (".MYSQL_MIN_VERSION.")");
- // Check some server variables
- $iMaxAllowedPacket = $oDBSource->GetServerVariable('max_allowed_packet');
- $iMaxUploadSize = utils::ConvertToBytes(ini_get('upload_max_filesize'));
- if ($iMaxAllowedPacket >= (500 + $iMaxUploadSize)) // Allow some space for the query + the file to upload
- {
- $oP->ok("MySQL server's max_allowed_packet is big enough.");
- }
- else if($iMaxAllowedPacket < $iMaxUploadSize)
- {
- $oP->warning("MySQL server's max_allowed_packet ($iMaxAllowedPacket) is not big enough. Please, consider setting it to at least ".(500 + $iMaxUploadSize).".");
- }
- $oP->log("Info - MySQL max_allowed_packet: $iMaxAllowedPacket");
- $iMaxConnections = $oDBSource->GetServerVariable('max_connections');
- if ($iMaxConnections < 5)
- {
- $oP->warning("MySQL server's max_connections ($iMaxConnections) is not enough. Please, consider setting it to at least 5.");
- }
- $oP->log("Info - MySQL max_connections: ".($oDBSource->GetServerVariable('max_connections')));
- }
- else
- {
- $oP->error("Error: Current MySQL version is ($sDBVersion), minimum required version (".MYSQL_MIN_VERSION.")");
- return false;
- }
- 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)
- {
- $oP->error("Error: Connection to '$sDBServer' as '$sDBUser' failed.");
- $oP->p($e->GetHtmlDesc());
- $aResult = false;
- }
- return $aResult;
-}
-
-/**
- * Helper function to interpret the name of a module
- * @param $sModuleId string Identifier of the module, in the form 'name/version'
- * @return array(name, version)
- */
-function GetModuleName($sModuleId)
-{
- if (preg_match('!^(.*)/(.*)$!', $sModuleId, $aMatches))
- {
- $sName = $aMatches[1];
- $sVersion = $aMatches[2];
- }
- else
- {
- $sName = $sModuleId;
- $sVersion = "";
- }
- return array($sName, $sVersion);
-}
-
-/**
- * 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 $bModelOnly boolean Whether or not to allow loading a data model with no corresponding DB
- * @return none
- */
-function InitDataModel(SetupWebPage $oP, $sConfigFileName, $bModelOnly = true)
-{
- require_once(APPROOT.'/core/log.class.inc.php');
- require_once(APPROOT.'/core/kpi.class.inc.php');
- require_once(APPROOT.'/core/coreexception.class.inc.php');
- require_once(APPROOT.'/core/dict.class.inc.php');
- require_once(APPROOT.'/core/attributedef.class.inc.php');
- require_once(APPROOT.'/core/filterdef.class.inc.php');
- require_once(APPROOT.'/core/stimulus.class.inc.php');
- require_once(APPROOT.'/core/MyHelpers.class.inc.php');
- require_once(APPROOT.'/core/expression.class.inc.php');
- require_once(APPROOT.'/core/cmdbsource.class.inc.php');
- require_once(APPROOT.'/core/sqlquery.class.inc.php');
- require_once(APPROOT.'/core/dbobject.class.php');
- require_once(APPROOT.'/core/dbobjectsearch.class.php');
- require_once(APPROOT.'/core/dbobjectset.class.php');
- require_once(APPROOT.'/application/cmdbabstract.class.inc.php');
- require_once(APPROOT.'/core/userrights.class.inc.php');
- require_once(APPROOT.'/setup/moduleinstallation.class.inc.php');
- $oP->log("Info - MetaModel::Startup from file '$sConfigFileName' (ModelOnly = $bModelOnly)");
-
- MetaModel::Startup($sConfigFileName, $bModelOnly);
-}
-/**
- * Helper function to create the database structure
- * @return boolean true on success, false otherwise
- */
-function CreateDatabaseStructure(SetupWebPage $oP, Config $oConfig, $sDBName, $sDBPrefix, $aSelectedModules)
-{
- InitDataModel($oP, TMP_CONFIG_FILE, true); // Allow the DB to NOT exist since we're about to create it !
- $oP->log('Info - CreateDatabaseStructure');
- if (strlen($sDBPrefix) > 0)
- {
- $oP->info("Creating the structure in '$sDBName' (table names prefixed by '$sDBPrefix').");
- }
- else
- {
- $oP->info("Creating the structure in '$sDBName'.");
- }
-
- //MetaModel::CheckDefinitions();
- if (!MetaModel::DBExists(/* bMustBeComplete */ false))
- {
- MetaModel::DBCreate();
- $oP->ok("Database structure successfully created.");
- }
- else
- {
- if (strlen($sDBPrefix) > 0)
- {
- $oP->error("Error: found iTop tables into the database '$sDBName' (prefix: '$sDBPrefix'). Please, try selecting another database instance or specify another prefix to prevent conflicting table names.");
- }
- else
- {
- $oP->error("Error: found iTop tables into the database '$sDBName'. Please, try selecting another database instance or specify a prefix to prevent conflicting table names.");
- }
- return false;
- }
-
- // Record main installation
- $oInstallRec = new ModuleInstallation();
- $oInstallRec->Set('name', 'itop');
- $oInstallRec->Set('version', ITOP_VERSION.'.'.ITOP_REVISION);
- $oInstallRec->Set('comment', "Done by the setup program\nBuilt on ".ITOP_BUILD_DATE);
- $oInstallRec->Set('parent_id', 0); // root module
- $iMainItopRecord = $oInstallRec->DBInsertNoReload();
-
- // Record installed modules
- //
- $aAvailableModules = GetAvailableModules($oP);
- foreach($aSelectedModules as $sModuleId)
- {
- $aModuleData = $aAvailableModules[$sModuleId];
- list($sName, $sVersion) = GetModuleName($sModuleId);
- $aComments = array();
- $aComments[] = 'Done by the setup program';
- if ($aModuleData['mandatory'])
- {
- $aComments[] = 'Mandatory';
- }
- else
- {
- $aComments[] = 'Optional';
- }
- if ($aModuleData['visible'])
- {
- $aComments[] = 'Visible (during the setup)';
- }
- else
- {
- $aComments[] = 'Hidden (selected automatically)';
- }
- foreach ($aModuleData['dependencies'] as $sDependOn)
- {
- $aComments[] = "Depends on module: $sDependOn";
- }
- $sComment = implode("\n", $aComments);
-
- $oInstallRec = new ModuleInstallation();
- $oInstallRec->Set('name', $sName);
- $oInstallRec->Set('version', $sVersion);
- $oInstallRec->Set('comment', $sComment);
- $oInstallRec->Set('parent_id', $iMainItopRecord);
- $oInstallRec->DBInsertNoReload();
- }
- // Database is created, installation has been tracked into it
- return true;
-}
-
-/**
- * Helper function to create and administrator account for iTop
- * @return boolean true on success, false otherwise
- */
-function CreateAdminAccount(SetupWebPage $oP, Config $oConfig, $sAdminUser, $sAdminPwd, $sLanguage)
-{
- $oP->log('Info - CreateAdminAccount');
-
- if (UserRights::CreateAdministrator($sAdminUser, $sAdminPwd, $sLanguage))
- {
- $oP->ok("Administrator account '$sAdminUser' created.");
- return true;
- }
- else
- {
- $oP->error("Failed to create the administrator account '$sAdminUser'.");
- return false;
- }
-}
-
-function ListModuleFiles($sRelDir, SetupWebPage $oP)
-{
- $sDirectory = APPROOT.'/'.$sRelDir;
- //echo "$sDirectory
\n";
- if ($hDir = opendir($sDirectory))
- {
- // This is the correct way to loop over the directory. (according to the documentation)
- while (($sFile = readdir($hDir)) !== false)
- {
- $aMatches = array();
- if (is_dir($sDirectory.'/'.$sFile))
- {
- if (($sFile != '.') && ($sFile != '..') && ($sFile != '.svn'))
- {
- ListModuleFiles($sRelDir.'/'.$sFile, $oP);
- }
- }
- else if (preg_match('/^module\.(.*).php$/i', $sFile, $aMatches))
- {
- $oP->SetModulePath($sRelDir);
- try
- {
- //echo "Loading: $sDirectory/$sFile...
\n";
- require_once($sDirectory.'/'.$sFile);
- //echo "Done.
\n";
- }
- catch(Exception $e)
- {
- // Continue...
- }
- }
- }
- closedir($hDir);
- }
- else
- {
- $oP->error("Data directory (".$sDirectory.") not found or not readable.");
- }
-}
-
-
-/**
- * Scans the ./data directory for XML files and output them as a Javascript array
- */
-function PopulateDataFilesList(SetupWebPage $oP, $aParamValues)
-{
-
- $oP->add("\n");
-}
-
-/**
- * Add some parameters as hidden inputs into a form
- * @param SetupWebpage $oP The page to insert the form elements into
- * @param Hash $aParamValues The pairs name/value to be stored in the form
- * @param Array $aExcludeParams A list of parameters to exclude from the previous hash
- */
-function AddParamsToForm(SetupWebpage $oP, $aParamValues, $aExcludeParams = array())
-{
- foreach($aParamValues as $sName => $value)
- {
- if(!in_array($sName, $aExcludeParams))
- {
- if (is_array($value))
- {
- foreach($value as $sKey => $sItem)
- {
- $oP->add(' ');
- }
- }
- else
- {
- $oP->add(' ');
- }
- }
- }
-}
-
-/**
- * Search (on the disk) for all defined iTop modules, load them and returns the list (as an array)
- * of the possible iTop modules to install
- * @param none
- * @return Hash A big array moduleID => ModuleData
- */
-function GetAvailableModules(SetupWebpage $oP)
-{
- clearstatcache();
- ListModuleFiles('modules', $oP);
- return $oP->GetModules();
-}
-
-/**
- * Build the config file from the parameters (especially the selected modules)
- */
-function BuildConfig(SetupWebpage $oP, Config &$oConfig, $aParamValues, $aAvailableModules)
-{
- // Initialize the arrays below with default values for the application...
- $aAddOns = $oConfig->GetAddOns();
- $aAppModules = $oConfig->GetAppModules();
- $aDataModels = $oConfig->GetDataModels();
- $aWebServiceCategories = $oConfig->GetWebServiceCategories();
- $aDictionaries = $oConfig->GetDictionaries();
- // Merge the values with the ones provided by the modules
- // Make sure when don't load the same file twice...
- foreach($aParamValues['module'] as $sModuleId)
- {
- $oP->log('Installed iTop module: '. $sModuleId);
- if (isset($aAvailableModules[$sModuleId]['datamodel']))
- {
- $aDataModels = array_unique(array_merge($aDataModels, $aAvailableModules[$sModuleId]['datamodel']));
- }
- if (isset($aAvailableModules[$sModuleId]['webservice']))
- {
- $aWebServiceCategories = array_unique(array_merge($aWebServiceCategories, $aAvailableModules[$sModuleId]['webservice']));
- }
- if (isset($aAvailableModules[$sModuleId]['dictionary']))
- {
- $aDictionaries = array_unique(array_merge($aDictionaries, $aAvailableModules[$sModuleId]['dictionary']));
- }
- if (isset($aAvailableModules[$sModuleId]['settings']))
- {
- foreach($aAvailableModules[$sModuleId]['settings'] as $sProperty => $value)
- {
- list($sName, $sVersion) = GetModuleName($sModuleId);
- $oConfig->SetModuleSetting($sName, $sProperty, $value);
- }
- }
- if (isset($aAvailableModules[$sModuleId]['installer']))
- {
- $sModuleInstallerClass = $aAvailableModules[$sModuleId]['installer'];
- if (!class_exists($sModuleInstallerClass))
- {
- throw new Exception("Wrong installer class: '$sModuleInstallerClass' is not a PHP class - Module: ".$aAvailableModules[$sModuleId]['label']);
- }
- if (!is_subclass_of($sModuleInstallerClass, 'ModuleInstallerAPI'))
- {
- throw new Exception("Wrong installer class: '$sModuleInstallerClass' is not derived from 'ModuleInstallerAPI' - Module: ".$aAvailableModules[$sModuleId]['label']);
- }
- $aCallSpec = array($sModuleInstallerClass, 'BeforeWritingConfig');
- $oConfig = call_user_func_array($aCallSpec, array($oConfig));
- }
- }
- $oConfig->SetAddOns($aAddOns);
- $oConfig->SetAppModules($aAppModules);
- $oConfig->SetDataModels($aDataModels);
- $oConfig->SetWebServiceCategories($aWebServiceCategories);
- $oConfig->SetDictionaries($aDictionaries);
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// Handling of the different steps of the setup wizard
-/////////////////////////////////////////////////////////////////////////////////////////////////////////////
-
-/**
- * Displays the welcome screen and check some basic prerequisites
- */
-function WelcomeAndCheckPrerequisites(SetupWebPage $oP, $aParamValues, $iCurrentStep)
-{
- $sNextOperation = 'step'.($iCurrentStep+1);
- $oP->add("iTop configuration wizard \n");
- $sVersionStringShort = GetITopVersion(true);
- $sVersionStringLong = GetITopVersion(false);
- $oP->set_title('Welcome to '.$sVersionStringShort);
- $oP->log($sVersionStringLong);
- $oP->add("Checking prerequisites \n");
- if (CheckPHPVersion($oP))
- {
- $oP->add("Next: Licence agreement \n");
- $oP->add("\n");
- }
-}
-
-function LicenceAcknowledgement($oP, $aParamValues, $iCurrentStep)
-{
- $sNextOperation = 'step'.($iCurrentStep+1);
-
- $oP->set_title('License agreement');
- $oP->add('iTop is released by Combodo SARL under the terms of the GPL V3 license. In order to use iTop you must accept the terms of this license. ');
- $oP->add("\n");
- $oP->add("\n");
-}
-
-/**
- * Display the form for the first step of the configuration wizard
- * which consists in the database server selection
- */
-function DatabaseServerSelection(SetupWebPage $oP, $aParamValues, $iCurrentStep)
-{
- $sNextOperation = 'step'.($iCurrentStep+1);
-
- $oP->add("\n");
-}
-
-/**
- * Display the form for the second step of the configuration wizard
- * which consists in
- * 1) Validating the parameters by connecting to the database server
- * 2) Prompting to select an existing database or to create a new one
- */
-function DatabaseInstanceSelection(SetupWebPage $oP, $aParamValues, $iCurrentStep, $oConfig)
-{
- $sNextOperation = 'step'.($iCurrentStep+1);
- $oP->set_title("Database instance selection\n");
- $oP->add("\n");
-}
-
-/**
- * Display the form to select the iTop modules to be installed
- */
-function ModulesSelection(SetupWebPage $oP, $aParamValues, $iCurrentStep, $oConfig)
-{
- $sNextOperation = 'step'.($iCurrentStep+1);
- $sPrevOperation = 'step'.($iCurrentStep-1);
-
- $sDBName = $aParamValues['db_name'];
- if ($sDBName == '')
- {
- $sDBName = $aParamValues['new_db_name'];
- }
- $sDBPrefix = $aParamValues['db_prefix'];
- $oConfig->SetDBName($sDBName);
- $oConfig->SetDBSubname($sDBPrefix);
- $oConfig->WriteToFile(TMP_CONFIG_FILE);
-
- $oP->add("\n");
- $oP->add_ready_script("$('.read-only').click( function() { $(this).attr('checked','checked'); } );");
-
-}
-/**
- * Display the form for the third step of the configuration wizard
- * which consists in
- * 1) Validating the parameters by connecting to the database server & selecting the database
- * 2) Creating the database structure
- * 3) Prompting for the admin account to be created
- */
-function AdminAccountDefinition(SetupWebPage $oP, $aParamValues, $iCurrentStep, Config $oConfig)
-{
- $sNextOperation = 'step'.($iCurrentStep+1);
- $oP->set_title("Administrator account creation");
- $oP->add("Creation of the database structure ");
- $oP->add("\n");
-}
-
-/**
- * Display the form for the fourth step of the configuration wizard
- * which consists in
- * 1) Creating the admin user account
- * 2) Prompting to load some sample data
- */
-function SampleDataSelection(SetupWebPage $oP, $aParamValues, $iCurrentStep, Config $oConfig)
-{
- $sNextOperation = 'step'.($iCurrentStep+1);
-
- $oP->set_title("Application initialization");
- $sAdminUser = $aParamValues['auth_user'];
- $sAdminPwd = $aParamValues['auth_pwd'];
- $sLanguage = $aParamValues['language'];
- $oConfig->SetDefaultLanguage($aParamValues['language']);
- $oConfig->WriteToFile(TMP_CONFIG_FILE);
-
- $oP->add("\n");
- // Hidden form submitted when moving on to the next page, once all the data files
- // have been processed
- $oP->add("\n");
- $oP->add("
\n");
- $oP->add_linked_script('./jquery.progression.js');
-
- PopulateDataFilesList($oP, $aParamValues);
-}
-/**
- * Display the form for the fifth (and final) step of the configuration wizard
- * which consists in
- * 1) Creating the final configuration file
- * 2) Prompting the user to make the file read-only
- */
-function SetupFinished(SetupWebPage $oP, $aParamValues, $iCurrentStep, Config $oConfig)
-{
- $sAuthUser = $aParamValues['auth_user'];
- $sAuthPwd = $aParamValues['auth_pwd'];
- try
- {
- $sSessionName = sprintf('iTop-%x', rand());
- $oConfig->Set('session_name', $sSessionName);
- session_name($sSessionName);
- session_start();
-
- // Write the final configuration file
- $oConfig->WriteToFile(FINAL_CONFIG_FILE);
-
- // Start the application
- InitDataModel($oP, FINAL_CONFIG_FILE, false); // Load model and startup DB
- if (UserRights::CheckCredentials($sAuthUser, $sAuthPwd))
- {
- UserRights::Login($sAuthUser);
- $_SESSION['auth_user'] = $sAuthUser;
- $_SESSION['login_mode'] = 'form'; // Will enable the "log-off button"
-
- // remove the tmp config file
- @unlink(TMP_CONFIG_FILE);
- // try to make the final config file read-only
- @chmod(FINAL_CONFIG_FILE, 0440); // Read-only for owner and group, nothing for others
-
- $oP->set_title("Setup complete");
- $oP->add("\n");
- }
- else
- {
- $oP->add("iTop configuration wizard \n");
- $oP->add("Step 5: Configuration completed \n");
-
- @unlink(FINAL_CONFIG_FILE); // remove the aborted config
- $oP->error("Error: Failed to login for user: '$sAuthUser'\n");
-
- $oP->add("\n");
- }
- }
- catch(Exception $e)
- {
- $oP->error("Error: unable to create the configuration file.");
- $oP->p($e->getHtmlDesc());
- $oP->p("Did you forget to remove the previous (read-only) configuration file ?");
- $oP->add("\n");
- }
-}
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// 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(TMP_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(TMP_CONFIG_FILE))." ' is writable for the web server.");
- $oP->output();
- exit;
- }
- if (!is_writable(dirname(TMP_CONFIG_FILE).'/setup'))
- {
- $oP->add("iTop configuration wizard \n");
- $oP->add("Fatal error \n");
- $oP->error("Error: the directory where to store temporary setup files is not writable.");
- $oP->p("The wizard cannot create operate. Please make sure that the directory '".realpath(dirname(TMP_CONFIG_FILE))."/setup ' is writable for the web server.");
- $oP->output();
- exit;
- }
-
-}
-try
-{
- $oConfig = new Config(TMP_CONFIG_FILE);
-}
-catch(Exception $e)
-{
- // We'll end here when the tmp config file does not exist. It's normal
- $oConfig = new Config(TMP_CONFIG_FILE, false /* Don't try to load it */);
-}
-try
-{
- // Set a long (at least 4 minutes) execution time for the setup to avoid timeouts during this phase
- ini_set('max_execution_time', max(240, ini_get('max_execution_time')));
- // While running the setup it is desirable to see any error that may happen
- ini_set('display_errors', true);
- ini_set('display_startup_errors', true);
-
- $aParams = array('licence_ok', 'db_server', 'db_user', 'db_pwd','db_name', 'new_db_name', 'db_prefix', 'module', 'sample_data', 'auth_user', 'auth_pwd', 'language');
- foreach($aParams as $sName)
- {
- $aParamValues[$sName] = utils::ReadParam($sName, '');
- }
-
- switch($sOperation)
- {
- case 'step0':
- $oP->no_cache();
- $oP->log("Info - ========= Wizard step 0 ========");
- WelcomeAndCheckPrerequisites($oP, $aParamValues, 0);
- break;
-
- case 'step1':
- $oP->no_cache();
- $oP->log("Info - ========= Wizard step 1 ========");
- LicenceAcknowledgement($oP, $aParamValues, 1);
- break;
-
- case 'step2':
- $oP->log("Info - ========= Wizard step 2 ========");
- DatabaseServerSelection($oP, $aParamValues, 2);
- break;
-
- case 'step3':
- $oP->no_cache();
- $oP->log("Info - ========= Wizard step 3 ========");
- DatabaseInstanceSelection($oP, $aParamValues, 3, $oConfig);
- break;
-
- case 'step4':
- $oP->no_cache();
- $oP->log("Info - ========= Wizard step 4 ========");
- ModulesSelection($oP, $aParamValues, 4, $oConfig);
- break;
-
-
- case 'step5':
- $oP->no_cache();
- $oP->log("Info - ========= Wizard step 5 ========");
- AdminAccountDefinition($oP, $aParamValues, 5, $oConfig);
- break;
-
- case 'step6':
- $oP->no_cache();
- $oP->log("Info - ========= Wizard step 6 ========");
- SampleDataSelection($oP, $aParamValues, 6, $oConfig);
- break;
-
- case 'step7':
- $oP->no_cache();
- $oP->log("Info - ========= Wizard step 7 ========");
- SetupFinished($oP, $aParamValues, 7, $oConfig);
- break;
-
- default:
- $oP->error("Error: unsupported operation '$sOperation'");
-
- }
-}
-catch(Exception $e)
-{
- $oP->error("Error: '".$e->getMessage()."'");
- $oP->add("<< Back \n");
-}
-catch(CoreException $e)
-{
- $oP->error("Error: '".$e->getHtmlDesc()."'");
- $oP->add("<< Back \n");
-}
-$oP->output();
-?>
+
+ * @author Romain Quetiez
+ * @author Denis Flaven
+ * @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
+ */
+
+require_once('../approot.inc.php');
+require_once(APPROOT.'/application/utils.inc.php');
+require_once(APPROOT.'/core/config.class.inc.php');
+require_once(APPROOT.'/core/log.class.inc.php');
+require_once(APPROOT.'/core/kpi.class.inc.php');
+require_once(APPROOT.'/core/cmdbsource.class.inc.php');
+require_once(APPROOT.'/setup/setuppage.class.inc.php');
+require_once(APPROOT.'/setup/moduleinstaller.class.inc.php');
+
+define('TMP_CONFIG_FILE', APPROOT.'/tmp-config-itop.php');
+define('FINAL_CONFIG_FILE', APPROOT.'/config-itop.php');
+define('PHP_MIN_VERSION', '5.2.0');
+define('MYSQL_MIN_VERSION', '5.0.0');
+define('MIN_MEMORY_LIMIT', 32*1024*1024);
+
+$sOperation = Utils::ReadParam('operation', 'step0');
+$oP = new SetupWebPage('iTop configuration wizard');
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Various helper function
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Get a nicely formatted version string
+ */
+function GetITopVersion($bShort = true)
+{
+ $sVersionString = '';
+ if ($bShort)
+ {
+ $sVersionString = "iTop Version ".ITOP_VERSION;
+ }
+ else
+ {
+ if (ITOP_REVISION == '$WCREV$')
+ {
+ // This is NOT a version built using the buil system, just display the main version
+ $sVersionString = "iTop Version ".ITOP_VERSION;
+ }
+ else
+ {
+ // This is a build made from SVN, let display the full information
+ $sVersionString = "iTop Version ".ITOP_VERSION." revision ".ITOP_REVISION.", built on: ".ITOP_BUILD_DATE;
+ }
+ }
+ return $sVersionString;
+}
+
+/**
+ * Helper function to retrieve the system's temporary directory
+ * Emulates sys_get_temp_dir if neeed (PHP < 5.2.1)
+ * @return string Path to the system's temp directory
+ */
+function GetTmpDir()
+{
+ // try to figure out what is the temporary directory
+ // prior to PHP 5.2.1 the function sys_get_temp_dir
+ // did not exist
+ if ( !function_exists('sys_get_temp_dir'))
+ {
+ if( $temp=getenv('TMP') ) return realpath($temp);
+ if( $temp=getenv('TEMP') ) return realpath($temp);
+ if( $temp=getenv('TMPDIR') ) return realpath($temp);
+ $temp=tempnam(__FILE__,'');
+ if (file_exists($temp))
+ {
+ unlink($temp);
+ return realpath(dirname($temp));
+ }
+ return null;
+ }
+ else
+ {
+ return realpath(sys_get_temp_dir());
+ }
+}
+
+/**
+ * Helper function to retrieve the directory where files are to be uploaded
+ * @return string Path to the temp directory used for uploading files
+ */
+function GetUploadTmpDir()
+{
+ $sPath = ini_get('upload_tmp_dir');
+ if (empty($sPath))
+ {
+ $sPath = GetTmpDir();
+ }
+ return $sPath;
+}
+
+/**
+ * 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(SetupWebPage $oP)
+{
+ $bResult = true;
+ $aErrors = array();
+ $aWarnings = array();
+ $aOk = array();
+
+ $oP->log('Info - CheckPHPVersion');
+ if (version_compare(phpversion(), PHP_MIN_VERSION, '>='))
+ {
+ $aOk [] = "The current PHP Version (".phpversion().") is greater than the minimum required version (".PHP_MIN_VERSION.")";
+ }
+ else
+ {
+ $aErrors[] = "Error: The current PHP Version (".phpversion().") is lower than the minimum required version (".PHP_MIN_VERSION.")";
+ $bResult = false;
+ }
+ $aMandatoryExtensions = array('mysql', 'iconv', 'simplexml', 'soap', 'hash', 'json', 'session', 'pcre', 'dom');
+ $aOptionalExtensions = array('mcrypt' => 'Strong encryption will not be used.',
+ 'ldap' => 'LDAP authentication will be disabled.');
+ asort($aMandatoryExtensions); // Sort the list to look clean !
+ ksort($aOptionalExtensions); // Sort the list to look clean !
+ $aExtensionsOk = array();
+ $aMissingExtensions = array();
+ $aMissingExtensionsLinks = array();
+ // First check the mandatory extensions
+ foreach($aMandatoryExtensions as $sExtension)
+ {
+ if (extension_loaded($sExtension))
+ {
+ $aExtensionsOk[] = $sExtension;
+ }
+ else
+ {
+ $aMissingExtensions[] = $sExtension;
+ $aMissingExtensionsLinks[] = "$sExtension ";
+ }
+ }
+ if (count($aExtensionsOk) > 0)
+ {
+ $aOk[] = "Required PHP extension(s): ".implode(', ', $aExtensionsOk).".";
+ }
+ if (count($aMissingExtensions) > 0)
+ {
+ $aErrors[] = "Missing PHP extension(s): ".implode(', ', $aMissingExtensionsLinks).".";
+ $bResult = false;
+ }
+ // Next check the optional extensions
+ $aExtensionsOk = array();
+ $aMissingExtensions = array();
+ foreach($aOptionalExtensions as $sExtension => $sMessage)
+ {
+ if (extension_loaded($sExtension))
+ {
+ $aExtensionsOk[] = $sExtension;
+ }
+ else
+ {
+ $aMissingExtensions[$sExtension] = $sMessage;
+ }
+ }
+ if (count($aExtensionsOk) > 0)
+ {
+ $aOk[] = "Optional PHP extension(s): ".implode(', ', $aExtensionsOk).".";
+ }
+ if (count($aMissingExtensions) > 0)
+ {
+ foreach($aMissingExtensions as $sExtension => $sMessage)
+ {
+ $aWarnings[] = "Missing optional PHP extension: $sExtension. ".$sMessage;
+ }
+ }
+ // Check some ini settings here
+ if (function_exists('php_ini_loaded_file')) // PHP >= 5.2.4
+ {
+ $sPhpIniFile = php_ini_loaded_file();
+ // Other included/scanned files
+ if ($sFileList = php_ini_scanned_files())
+ {
+ if (strlen($sFileList) > 0)
+ {
+ $aFiles = explode(',', $sFileList);
+
+ foreach ($aFiles as $sFile)
+ {
+ $sPhpIniFile .= ', '.trim($sFile);
+ }
+ }
+ }
+ $oP->log("Info - php.ini file(s): '$sPhpIniFile'");
+ }
+ else
+ {
+ $sPhpIniFile = 'php.ini';
+ }
+ if (!ini_get('file_uploads'))
+ {
+ $aErrors[] = "Files upload is not allowed on this server (file_uploads = ".ini_get('file_uploads').").";
+ $bResult = false;
+ }
+
+ $sUploadTmpDir = GetUploadTmpDir();
+ if (empty($sUploadTmpDir))
+ {
+ $sUploadTmpDir = '/tmp';
+ $aErrors[] = "Temporary directory for files upload is not defined (upload_tmp_dir), assuming that $sUploadTmpDir is used.";
+ }
+ // check that the upload directory is indeed writable from PHP
+ if (!empty($sUploadTmpDir))
+ {
+ if (!file_exists($sUploadTmpDir))
+ {
+ $aErrors[] = "Temporary directory for files upload ($sUploadTmpDir) does not exist or cannot be read by PHP.";
+ $bResult = false;
+ }
+ else if (!is_writable($sUploadTmpDir))
+ {
+ $aErrors[] = "Temporary directory for files upload ($sUploadTmpDir) is not writable.";
+ $bResult = false;
+ }
+ else
+ {
+ $oP->log("Info - Temporary directory for files upload ($sUploadTmpDir) is writable.");
+ }
+ }
+
+
+ if (!ini_get('upload_max_filesize'))
+ {
+ $aErrors[] = "File upload is not allowed on this server (file_uploads = ".ini_get('file_uploads').").";
+ }
+
+ $iMaxFileUploads = ini_get('max_file_uploads');
+ if (!empty($iMaxFileUploads) && ($iMaxFileUploads < 1))
+ {
+ $aErrors[] = "File upload is not allowed on this server (max_file_uploads = ".ini_get('max_file_uploads').").";
+ $bResult = false;
+ }
+ $oP->log("Info - upload_max_filesize: ".ini_get('upload_max_filesize'));
+ $oP->log("Info - max_file_uploads: ".ini_get('max_file_uploads'));
+
+ // Check some more ini settings here, needed for file upload
+ if (get_magic_quotes_gpc())
+ {
+ $aErrors[] = "'magic_quotes_gpc' is set to On. Please turn it Off before continuing. You may want to check the PHP configuration file(s): '$sPhpIniFile'. Be aware that this setting can also be overridden in the apache configuration.";
+ $bResult = false;
+ }
+
+ $sMemoryLimit = trim(ini_get('memory_limit'));
+ if (empty($sMemoryLimit))
+ {
+ // On some PHP installations, memory_limit does not exist as a PHP setting!
+ // (encountered on a 5.2.0 under Windows)
+ // In that case, ini_set will not work, let's keep track of this and proceed anyway
+ $aWarnings[] = "No memory limit has been defined in this instance of PHP";
+ }
+ else
+ {
+ // Check that the limit will allow us to load the data
+ //
+ $iMemoryLimit = utils::ConvertToBytes($sMemoryLimit);
+ if ($iMemoryLimit < MIN_MEMORY_LIMIT)
+ {
+ $aErrors[] = "memory_limit ($iMemoryLimit) is too small, the minimum value to run iTop is ".MIN_MEMORY_LIMIT.".";
+ $bResult = false;
+ }
+ else
+ {
+ $oP->log_info("memory_limit is $iMemoryLimit, ok.");
+ }
+ }
+
+ if (!$bResult)
+ {
+ $sTitle = 'Checking prerequisites: Failed !';
+ }
+ else
+ {
+ if (count($aWarnings) > 0)
+ {
+ $sTitle = ' Checking prerequisites: Warning (show details) ';
+ $oP->add_ready_script("$('#prereq_details').hide();\n");
+ }
+ else
+ {
+ $sTitle = ' Checking prerequisites: Ok (show details) ';
+ $oP->add_ready_script("$('#prereq_details').hide();\n");
+ }
+ }
+ $oP->add("$sTitle \n");
+ $oP->add("\n");
+ foreach($aErrors as $sError)
+ {
+ $oP->error($sError);
+ //$oP->add_ready_script("$('#prereq_details').show();");
+ }
+ foreach($aWarnings as $sWarning)
+ {
+ $oP->warning($sWarning);
+ }
+ foreach($aOk as $sOk)
+ {
+ $oP->ok($sOk);
+ }
+ $oP->add("
\n");
+ return $bResult;
+}
+
+/**
+ * Helper function check the connection to the database and (if connected) to enumerate
+ * the existing databases
+ * @return Array The list of databases found in the server
+ */
+function CheckServerConnection(SetupWebPage $oP, $sDBServer, $sDBUser, $sDBPwd)
+{
+ $aResult = array();
+ $oP->log('Info - CheckServerConnection');
+ try
+ {
+ $oDBSource = new CMDBSource;
+ $oDBSource->Init($sDBServer, $sDBUser, $sDBPwd);
+ $oP->ok("Connection to '$sDBServer' as '$sDBUser' successful.");
+
+ $oP->log("Info - User privileges: ".($oDBSource->GetRawPrivileges()));
+
+ $sDBVersion = $oDBSource->GetDBVersion();
+ if (version_compare($sDBVersion, MYSQL_MIN_VERSION, '>='))
+ {
+ $oP->ok("Current MySQL version ($sDBVersion), greater than minimum required version (".MYSQL_MIN_VERSION.")");
+ // Check some server variables
+ $iMaxAllowedPacket = $oDBSource->GetServerVariable('max_allowed_packet');
+ $iMaxUploadSize = utils::ConvertToBytes(ini_get('upload_max_filesize'));
+ if ($iMaxAllowedPacket >= (500 + $iMaxUploadSize)) // Allow some space for the query + the file to upload
+ {
+ $oP->ok("MySQL server's max_allowed_packet is big enough.");
+ }
+ else if($iMaxAllowedPacket < $iMaxUploadSize)
+ {
+ $oP->warning("MySQL server's max_allowed_packet ($iMaxAllowedPacket) is not big enough. Please, consider setting it to at least ".(500 + $iMaxUploadSize).".");
+ }
+ $oP->log("Info - MySQL max_allowed_packet: $iMaxAllowedPacket");
+ $iMaxConnections = $oDBSource->GetServerVariable('max_connections');
+ if ($iMaxConnections < 5)
+ {
+ $oP->warning("MySQL server's max_connections ($iMaxConnections) is not enough. Please, consider setting it to at least 5.");
+ }
+ $oP->log("Info - MySQL max_connections: ".($oDBSource->GetServerVariable('max_connections')));
+ }
+ else
+ {
+ $oP->error("Error: Current MySQL version is ($sDBVersion), minimum required version (".MYSQL_MIN_VERSION.")");
+ return false;
+ }
+ 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)
+ {
+ $oP->error("Error: Connection to '$sDBServer' as '$sDBUser' failed.");
+ $oP->p($e->GetHtmlDesc());
+ $aResult = false;
+ }
+ return $aResult;
+}
+
+/**
+ * Scans the ./data directory for XML files and output them as a Javascript array
+ */
+function PopulateDataFilesList(SetupWebPage $oP, $aParamValues, $oConfig)
+{
+
+ $oP->add("\n");
+}
+
+/**
+ * Add some parameters as hidden inputs into a form
+ * @param SetupWebpage $oP The page to insert the form elements into
+ * @param Hash $aParamValues The pairs name/value to be stored in the form
+ * @param Array $aExcludeParams A list of parameters to exclude from the previous hash
+ */
+function AddParamsToForm(SetupWebpage $oP, $aParamValues, $aExcludeParams = array())
+{
+ foreach($aParamValues as $sName => $value)
+ {
+ if(!in_array($sName, $aExcludeParams))
+ {
+ AddHiddenParam($oP, $sName, $value);
+ }
+ }
+}
+
+/**
+ * Add a hidden field to store the specified parameter
+ * @param $sName string Name of the parameter
+ * @param $value mixed Value of the parameter
+ */
+function AddHiddenParam($oP, $sName, $value)
+{
+ if (is_array($value))
+ {
+ foreach($value as $sKey => $sItem)
+ {
+ $oP->add(' ');
+ }
+ }
+ else
+ {
+ $oP->add(' ');
+ }
+}
+
+/**
+ * Build the config file from the parameters (especially the selected modules)
+ */
+function BuildConfig(SetupWebpage $oP, Config &$oConfig, $aParamValues, $aAvailableModules)
+{
+ // Initialize the arrays below with default values for the application...
+ $aAddOns = $oConfig->GetAddOns();
+ $aAppModules = $oConfig->GetAppModules();
+ $aDataModels = $oConfig->GetDataModels();
+ $aWebServiceCategories = $oConfig->GetWebServiceCategories();
+ $aDictionaries = $oConfig->GetDictionaries();
+ // Merge the values with the ones provided by the modules
+ // Make sure when don't load the same file twice...
+ foreach($aParamValues['module'] as $sModuleId)
+ {
+ $oP->log('Installed iTop module: '. $sModuleId);
+ if (isset($aAvailableModules[$sModuleId]['datamodel']))
+ {
+ $aDataModels = array_unique(array_merge($aDataModels, $aAvailableModules[$sModuleId]['datamodel']));
+ }
+ if (isset($aAvailableModules[$sModuleId]['webservice']))
+ {
+ $aWebServiceCategories = array_unique(array_merge($aWebServiceCategories, $aAvailableModules[$sModuleId]['webservice']));
+ }
+ if (isset($aAvailableModules[$sModuleId]['dictionary']))
+ {
+ $aDictionaries = array_unique(array_merge($aDictionaries, $aAvailableModules[$sModuleId]['dictionary']));
+ }
+ if (isset($aAvailableModules[$sModuleId]['settings']))
+ {
+ foreach($aAvailableModules[$sModuleId]['settings'] as $sProperty => $value)
+ {
+ list($sName, $sVersion) = GetModuleName($sModuleId);
+ $oConfig->SetModuleSetting($sName, $sProperty, $value);
+ }
+ }
+ if (isset($aAvailableModules[$sModuleId]['installer']))
+ {
+ $sModuleInstallerClass = $aAvailableModules[$sModuleId]['installer'];
+ if (!class_exists($sModuleInstallerClass))
+ {
+ throw new Exception("Wrong installer class: '$sModuleInstallerClass' is not a PHP class - Module: ".$aAvailableModules[$sModuleId]['label']);
+ }
+ if (!is_subclass_of($sModuleInstallerClass, 'ModuleInstallerAPI'))
+ {
+ throw new Exception("Wrong installer class: '$sModuleInstallerClass' is not derived from 'ModuleInstallerAPI' - Module: ".$aAvailableModules[$sModuleId]['label']);
+ }
+ $aCallSpec = array($sModuleInstallerClass, 'BeforeWritingConfig');
+ $oConfig = call_user_func_array($aCallSpec, array($oConfig));
+ }
+ }
+ $oConfig->SetAddOns($aAddOns);
+ $oConfig->SetAppModules($aAppModules);
+ $oConfig->SetDataModels($aDataModels);
+ $oConfig->SetWebServiceCategories($aWebServiceCategories);
+ $oConfig->SetDictionaries($aDictionaries);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Handling of the different steps of the setup wizard
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Displays the welcome screen and check some basic prerequisites
+ */
+function WelcomeAndCheckPrerequisites(SetupWebPage $oP, $aParamValues, $iCurrentStep)
+{
+ $sNextOperation = 'step'.($iCurrentStep+1);
+ $aParamValues['previous_step'] = 0;
+
+ $oP->add("iTop configuration wizard \n");
+ $sVersionStringShort = GetITopVersion(true);
+ $sVersionStringLong = GetITopVersion(false);
+ $oP->set_title('Welcome to '.$sVersionStringShort);
+ $oP->log($sVersionStringLong);
+ $aPreviousParams = array();
+ $oP->add("\n");
+ }
+}
+
+function LicenceAcknowledgement($oP, $aParamValues, $iCurrentStep)
+{
+ $sNextOperation = 'step'.($iCurrentStep+1);
+ $iPrevStep = 0;
+ $aParamValues['previous_step'] = $iCurrentStep; // Come back here
+
+ $oP->set_title('License agreement');
+ $oP->add('iTop is released by Combodo SARL under the terms of the GPL V3 license. In order to use iTop you must accept the terms of this license. ');
+ $oP->add("\n");
+ $oP->add("\n");
+}
+
+/**
+ * Display the form for the first step of the configuration wizard
+ * which consists in the database server selection
+ */
+function DatabaseServerSelection(SetupWebPage $oP, $aParamValues, $iCurrentStep)
+{
+ $sNextOperation = 'step'.($iCurrentStep+1);
+ $iPrevStep = 1;
+ $aParamValues['previous_step'] = $iCurrentStep; // Come back here
+
+ $oP->add("\n");
+}
+
+/**
+ * Display the form for the second step of the configuration wizard
+ * which consists in
+ * 1) Validating the parameters by connecting to the database server
+ * 2) Prompting to select an existing database or to create a new one
+ */
+function DatabaseInstanceSelection(SetupWebPage $oP, $aParamValues, $iCurrentStep, $oConfig)
+{
+ $sNextOperation = 'step'.($iCurrentStep+1);
+ $iPrevStep = 2;
+ $aParamValues['previous_step'] = $iCurrentStep; // Come back here
+
+ $oP->set_title("Database instance selection\n");
+ $oP->add("\n");
+}
+
+/**
+ * Display the form to select the iTop modules to be installed
+ */
+function ModulesSelection(SetupWebPage $oP, $aParamValues, $iCurrentStep, $oConfig)
+{
+ $sNextOperation = 'step'.($iCurrentStep+1);
+ $aParamValues['previous_step'] = $iCurrentStep; // Come back here
+
+ $sDBName = $aParamValues['db_name'];
+ if ($sDBName == '')
+ {
+ $sDBName = $aParamValues['new_db_name'];
+ }
+
+ $sDBPrefix = $aParamValues['db_prefix'];
+ $oConfig->SetDBName($sDBName);
+ $oConfig->SetDBSubname($sDBPrefix);
+ $oConfig->WriteToFile(TMP_CONFIG_FILE);
+
+ $oP->add("\n");
+ return;
+ }
+ $oP->add("Customize your iTop installation to fit your needs \n");
+ $oP->add("Select the iTop modules you want to install or upgrade: \n");
+ }
+ else
+ {
+ $iPrevStep = 3; // depends on where we came from
+ if (!empty($aAvailableModules['iTop']['version_db']))
+ {
+ $oP->error("A instance of iTop already exists. Please select the \"Upgrade\" mode to upgrade it.\n");
+ $oP->add("\n");
+ $oP->add("<< Back \n");
+ $oP->add(" \n");
+ $oP->add("
\n");
+ $oP->add("\n");
+ return;
+ }
+ $oP->add("Customize your iTop installation to fit your needs \n");
+ $oP->add("Select the iTop modules you want to install: \n");
+ }
+ $oP->add("");
+ $oP->add(" \n");
+ if ($aParamValues['mode'] == 'upgrade')
+ {
+ $oP->add("Next: Upgrade summary \n");
+ AddHiddenParam($oP, 'operation', 'step6');
+ }
+ else
+ {
+ $oP->add("Next: Administrator account definition \n");
+ AddHiddenParam($oP, 'operation', 'step5');
+ }
+ $oP->add("\n");
+ $oP->add("<< Back \n");
+ $oP->add("Next >> \n");
+ $oP->add("
\n");
+ $oP->add("\n");
+ $oP->add_ready_script("$('.read-only').click( function() { $(this).attr('checked','checked'); } );");
+
+}
+/**
+ * Display the form for the third step of the configuration wizard
+ * which consists in
+ * 1) Validating the parameters by connecting to the database server & selecting the database
+ * 2) Creating the database structure
+ * 3) Prompting for the admin account to be created
+ */
+function AdminAccountDefinition(SetupWebPage $oP, $aParamValues, $iCurrentStep, Config $oConfig)
+{
+ $sNextOperation = 'step'.($iCurrentStep+1);
+ $iPrevStep = 4;
+ $aParamValues['previous_step'] = $iCurrentStep; // Come back here
+
+ $oP->set_title("Administrator account definition");
+ $oP->add("\n");
+}
+
+/**
+ * Display the form for the fourth step of the configuration wizard
+ * which consists in
+ * 1) Creating the admin user account
+ * 2) Prompting to load some sample data
+ */
+function SampleDataSelection(SetupWebPage $oP, $aParamValues, $iCurrentStep, Config $oConfig)
+{
+ $sNextOperation = 'step7';
+ if ($aParamValues['mode'] == 'upgrade')
+ {
+ $iPrevStep = 4;
+ }
+ else
+ {
+ $iPrevStep = 5;
+ }
+
+ $oP->set_title("Application initialization");
+ $sAdminUser = $aParamValues['auth_user'];
+ $sAdminPwd = $aParamValues['auth_pwd'];
+ $sLanguage = $aParamValues['language'];
+ if (($aParamValues['mode'] == 'install') || $oConfig->GetDefaultLanguage() == '')
+ {
+ $oConfig->SetDefaultLanguage($aParamValues['language']);
+ }
+ $aAvailableModules = AnalyzeInstallation($oConfig);
+ BuildConfig($oP, $oConfig, $aParamValues, $aAvailableModules); // Load all the includes based on the modules selected
+
+ // in case of upgrade, the value is already present in the config file
+ $oConfig->WriteToFile(TMP_CONFIG_FILE);
+
+ $oP->add("\n");
+ // Hidden form submitted when moving on to the next page, once all the data files
+ // have been processed
+ $oP->add("\n");
+}
+
+/**
+ * Displays the summary of the actions to be taken
+ */
+function DisplaySummary(SetupWebPage $oP, $aParamValues, $iCurrentStep, Config $oConfig)
+{
+ $sMode = $aParamValues['mode'];
+ $aAvailableModules = AnalyzeInstallation($oConfig);
+ BuildConfig($oP, $oConfig, $aParamValues, $aAvailableModules); // Load all the includes based on the modules selected
+ $oConfig->WriteToFile(TMP_CONFIG_FILE);
+ InitDataModel(TMP_CONFIG_FILE, true); // Needed to know the available languages
+
+ $aInstall = array();
+ $aUpgrade = array();
+ $aUninstall = array();
+ $aUnchanged = array();
+ switch($sMode)
+ {
+ case 'install':
+ foreach($aAvailableModules as $sModuleId => $aModule)
+ {
+ if (($sModuleId != 'iTop') && $aModule['visible'])
+ {
+ if (in_array($sModuleId, $aParamValues['module']))
+ {
+ $aInstall[$sModuleId] = $aModule;
+ }
+ }
+ }
+ $oP->set_title('Installation Summary');
+ $oP->add("iTop version ".$aAvailableModules['iTop']['version_code']." will be installed in the database: ".$oConfig->GetDBName()." on server: ".$oConfig->GetDBHost().".".' ');
+ $oP->add('');
+ if (count($aInstall) > 0)
+ {
+ $oP->add('
Modules to install ');
+ foreach($aInstall as $sModuleId => $aModule)
+ {
+ $oP->p('
'.$aModule['label'].' version '.$aModule['version_code']);
+ }
+ }
+ $oP->add('
Sample data ');
+ if ($aParamValues['sample_data'] != 'no')
+ {
+ $oP->p('Sample data will be loaded for the new modules installed.');
+ }
+ else
+ {
+ $oP->p('No sample data will be loaded.');
+ }
+ $oP->add('
Administrator account ');
+ $oP->p('Login:'.htmlentities($aParamValues['auth_user'], ENT_QUOTES, 'UTF-8'));
+ $oP->add('
Default application language: ');
+ $aAvailableLanguages = Dict::GetLanguages();
+ $oP->p($aAvailableLanguages[$aParamValues['language']]['description']." (".$aAvailableLanguages[$aParamValues['language']]['localized_description'].")");
+ $oP->add('
');
+
+ $oP->add("\n");
+ break;
+
+ case 'upgrade':
+
+ foreach($aAvailableModules as $sModuleId => $aModule)
+ {
+ if (($sModuleId != 'iTop') && $aModule['visible'])
+ {
+ if (in_array($sModuleId, $aParamValues['module']))
+ {
+ if (empty($aModule['version_db']))
+ {
+ $aInstall[$sModuleId] = $aModule;
+ }
+ else if ($aModule['version_db'] == $aModule['version_code'])
+ {
+ $aUnchanged[$sModuleId] = $aModule;
+ }
+ else
+ {
+ // Consider it's an upgrade... TO DO: handle downgrades ??
+ $aUpgrade[$sModuleId] = $aModule;
+ }
+ }
+ else if (!empty($aModule['version_db']))
+ {
+ $aUninstall[$sModuleId] = $aModule;
+ }
+ // Else do nothing: the module was not installed and is not selected
+ }
+ }
+ $oP->set_title('Upgrade Summary');
+ $oP->add("iTop instance: database: ".$oConfig->GetDBName()." on server: ".$oConfig->GetDBHost().", version ".$aAvailableModules['iTop']['version_db'].' ');
+ $oP->add('Will be upgraded to '.$aAvailableModules['iTop']['version_code'].' ');
+ $oP->add('');
+ if (count($aUpgrade) > 0)
+ {
+ $oP->add('
Modules to upgrade ');
+ foreach($aUpgrade as $sModuleId => $aModule)
+ {
+ $oP->add('
'.$aModule['label'].' version '.$aModule['version_db'].' to version '.$aModule['version_code']);
+ }
+ }
+ if (count($aInstall) > 0)
+ {
+ $oP->add('
Modules to install ');
+ foreach($aInstall as $sModuleId => $aModule)
+ {
+ $oP->p('
'.$aModule['label'].' version '.$aModule['version_code']);
+ }
+ }
+ if (count($aUninstall) > 0)
+ {
+ $oP->add('
Modules to remove ');
+ foreach($aUninstall as $sModuleId => $aModule)
+ {
+ $oP->p('
'.$aModule['label'].' '.$sModuleId.' version '.$aModule['version_db']);
+ }
+ }
+ if (count($aUnchanged) > 0)
+ {
+ $oP->add('
Modules that will remain unchanged ');
+ foreach($aUnchanged as $sModuleId => $aModule)
+ {
+ $oP->p('
'.$aModule['label'].' version '.$aModule['version_db']);
+ }
+ }
+ $oP->add('
Sample data ');
+ if ($aParamValues['sample_data'] != 'no')
+ {
+ $oP->p('Sample data will be loaded for the new modules installed.');
+ }
+ else
+ {
+ $oP->p('No sample data will be loaded.');
+ }
+ $oP->add('
');
+ $oP->add("\n");
+ break;
+
+ default:
+ $oP->error("Unsupported mode $sMode");
+ }
+ $oP->add("
\n");
+ $oP->add_linked_script('./jquery.progression.js');
+ PopulateDataFilesList($oP, $aParamValues, $oConfig);
+ $oP->add_ready_script(
+<<Get('session_name');
+ if ($sSessionName != '')
+ {
+ $sSessionName = sprintf('iTop-%x', rand());
+ $oConfig->Set('session_name', $sSessionName);
+ }
+ session_name($sSessionName);
+ session_start();
+
+ // Migration: force utf8_unicode_ci as the collation to make the global search
+ // NON case sensitive
+ $oConfig->SetDBCollation('utf8_unicode_ci');
+
+
+ // Write the final configuration file
+ $oConfig->WriteToFile(FINAL_CONFIG_FILE);
+
+ // Start the application
+ InitDataModel(FINAL_CONFIG_FILE, false, true); // Load model, startup DB and load the cache
+ if ($aParamValues['mode'] == 'install')
+ {
+ if (UserRights::CheckCredentials($sAuthUser, $sAuthPwd))
+ {
+ UserRights::Login($sAuthUser);
+ $_SESSION['auth_user'] = $sAuthUser;
+ $_SESSION['login_mode'] = 'form'; // Will enable the "log-off button"
+ }
+ else
+ {
+ $oP->add("iTop configuration wizard \n");
+ $oP->add("Step 5: Configuration completed \n");
+
+ @unlink(FINAL_CONFIG_FILE); // remove the aborted config
+ $oP->error("Error: Failed to login for user: '$sAuthUser'\n");
+
+ $oP->add("\n");
+ return;
+ }
+ }
+
+ // remove the tmp config file
+ @unlink(TMP_CONFIG_FILE);
+ // try to make the final config file read-only
+ @chmod(FINAL_CONFIG_FILE, 0440); // Read-only for owner and group, nothing for others
+
+ $oP->set_title("Setup complete");
+ $oP->add("\n");
+ }
+ catch(Exception $e)
+ {
+ $oP->error("Error: unable to create the configuration file.");
+ $oP->p($e->getHtmlDesc());
+ $oP->p("Did you forget to remove the previous (read-only) configuration file ?");
+ $oP->add("\n");
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Main program
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+clearstatcache(); // Make sure we know what we are doing !
+// Set a long (at least 4 minutes) execution time for the setup to avoid timeouts during this phase
+ini_set('max_execution_time', max(240, ini_get('max_execution_time')));
+// While running the setup it is desirable to see any error that may happen
+ini_set('display_errors', true);
+ini_set('display_startup_errors', true);
+
+$aParams = array('mode', 'previous_step', 'licence_ok', 'db_server', 'db_user', 'db_pwd','db_name', 'new_db_name', 'db_prefix', 'module', 'sample_data', 'auth_user', 'auth_pwd', 'language');
+foreach($aParams as $sName)
+{
+ $aParamValues[$sName] = utils::ReadParam($sName, '');
+}
+
+if (file_exists(FINAL_CONFIG_FILE))
+{
+ // The configuration file already exists
+ if (!is_writable(FINAL_CONFIG_FILE))
+ {
+ $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 modify the configuration file for you. If you want to upgrade iTop, please make sure that the file '".realpath(FINAL_CONFIG_FILE)." ' can be modified by the web server.");
+ $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(TMP_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(TMP_CONFIG_FILE))." ' is writable for the web server.");
+ $oP->output();
+ exit;
+ }
+ if (!is_writable(dirname(TMP_CONFIG_FILE).'/setup'))
+ {
+ $oP->add("iTop configuration wizard \n");
+ $oP->add("Fatal error \n");
+ $oP->error("Error: the directory where to store temporary setup files is not writable.");
+ $oP->p("The wizard cannot create operate. Please make sure that the directory '".realpath(dirname(TMP_CONFIG_FILE))."/setup ' is writable for the web server.");
+ $oP->output();
+ exit;
+ }
+
+}
+try
+{
+ $oConfig = new Config(TMP_CONFIG_FILE);
+}
+catch(Exception $e)
+{
+ // We'll end here when the tmp config file does not exist. It's normal
+ $oConfig = new Config(TMP_CONFIG_FILE, false /* Don't try to load it */);
+}
+try
+{
+ switch($sOperation)
+ {
+ case 'step0':
+ $oP->no_cache();
+ $oP->log("Info - ========= Wizard step 0 ========");
+ WelcomeAndCheckPrerequisites($oP, $aParamValues, 0);
+ break;
+
+ case 'step1':
+ $oP->no_cache();
+ $oP->log("Info - ========= Wizard step 1 ========");
+ LicenceAcknowledgement($oP, $aParamValues, 1);
+ break;
+
+ case 'step2':
+ $oP->log("Info - ========= Wizard step 2 ========");
+ DatabaseServerSelection($oP, $aParamValues, 2);
+ break;
+
+ case 'step3':
+ $oP->no_cache();
+ $oP->log("Info - ========= Wizard step 3 ========");
+ DatabaseInstanceSelection($oP, $aParamValues, 3, $oConfig);
+ break;
+
+ case 'step4':
+ $oP->no_cache();
+ $oP->log("Info - ========= Wizard step 4 ========");
+ ModulesSelection($oP, $aParamValues, 4, $oConfig);
+ break;
+
+ case 'step5':
+ $oP->no_cache();
+ $oP->log("Info - ========= Wizard step 5 ========");
+ AdminAccountDefinition($oP, $aParamValues, 5, $oConfig);
+ break;
+
+ case 'step6':
+ $oP->no_cache();
+ $oP->log("Info - ========= Wizard step 6 ========");
+ SampleDataSelection($oP, $aParamValues, 6, $oConfig);
+ break;
+
+ case 'step7':
+ $oP->no_cache();
+ $oP->log("Info - ========= Wizard step 7 ========");
+ DisplaySummary($oP, $aParamValues, 7, $oConfig);
+ break;
+
+ case 'step8':
+ $oP->no_cache();
+ $oP->log("Info - ========= Wizard step 8 ========");
+ SetupFinished($oP, $aParamValues, 8, $oConfig);
+ break;
+
+ default:
+ $oP->error("Error: unsupported operation '$sOperation'");
+
+ }
+}
+catch(Exception $e)
+{
+ $oP->error("Error: '".$e->getMessage()."'");
+ $oP->add("<< Back \n");
+}
+catch(CoreException $e)
+{
+ $oP->error("Error: '".$e->getHtmlDesc()."'");
+ $oP->add("<< Back \n");
+}
+$oP->output();
+?>
\ No newline at end of file
diff --git a/setup/moduleinstaller.class.inc.php b/setup/moduleinstaller.class.inc.php
index cf77814ad..a2b5b753d 100644
--- a/setup/moduleinstaller.class.inc.php
+++ b/setup/moduleinstaller.class.inc.php
@@ -31,7 +31,11 @@ abstract class ModuleInstallerAPI
return $oConfiguration;
}
- public static function AfterDatabaseCreation(Config $oConfiguration)
+ public static function BeforeDatabaseCreation(Config $oConfiguration, $sPreviousVersion, $sCurrentVersion)
+ {
+ }
+
+ public static function AfterDatabaseCreation(Config $oConfiguration, $sPreviousVersion, $sCurrentVersion)
{
}
}
diff --git a/setup/setup.js b/setup/setup.js
index d32066e51..35988ec1c 100644
--- a/setup/setup.js
+++ b/setup/setup.js
@@ -7,7 +7,7 @@ function NameIsValid(name)
function DoGoBack(iStep)
{
- $('input[name=operation]').val('step'+(iStep-1));
+ $('input[name=operation]').val('step'+iStep);
$('#theForm').submit(); // Submit the form
return true;
}
@@ -17,6 +17,14 @@ function DoSubmit(sMsg, iStep)
var bResult = true;
switch(iStep)
{
+ case 0: // Select either install or upgrade or nothing to select...
+ if ( ($("input:radio").length > 0) && ($("input:radio:checked").length < 1))
+ {
+ alert('Please select either install or upgrade');
+ bResult = false;
+ }
+ break;
+
case 1: // Licence agreement
if ($('#licence_ok:checked').length < 1)
{
@@ -94,8 +102,11 @@ function DoSubmit(sMsg, iStep)
}
break;
- case 6: // Asynchronous load of data
- bResult = DoLoadDataAsynchronous();
+ case 6: // Sample data selection
+ break;
+
+ case 7: // Display Summary: launch DoUpdateDBSchema to start the asynchronous update
+ bResult = DoUpdateDBSchema();
break;
// Email test page
@@ -113,33 +124,108 @@ function DoSubmit(sMsg, iStep)
return bResult;
}
+function DoUpdateDBSchema()
+{
+ try
+ {
+ // Call the asynchronous page that performs the creation/update of the DB Schema
+ $('#log').html('');
+ $('#setup').block({message: 'Updating DB schema...
0%
'});
+ $('#progress').progression( {Current:5, Maximum: 100, aBackgroundImg: 'orange-progress.gif', aTextColor: '#000000'} );
+ $('#log').load( 'ajax.dataloader.php',
+ {
+ 'operation': 'update_db_schema',
+ 'selected_modules': GetSelectedModules(),
+ 'mode': $(':input[name=mode]').val()
+ },
+ DoUpdateProfiles, 'html');
+ }
+ catch(err)
+ {
+ alert('An exception occured: '+err);
+ }
+ return false; // Do NOT submit the form yet
+}
+
+function DoUpdateProfiles(response, status, xhr)
+{
+ if (status == 'error')
+ {
+ $('#setup').unblock();
+ return; // An error occurred !
+ }
+ try
+ {
+ // Call the asynchronous page that performs the creation/update of the DB Schema
+ $('#log').html('');
+ $('#setup').block({message: 'Updating Profiles...
0%
'});
+ $('#progress').progression( {Current:40, Maximum: 100, aBackgroundImg: 'orange-progress.gif', aTextColor: '#000000'} );
+ $('#log').load( 'ajax.dataloader.php',
+ {
+ 'operation': 'after_db_create',
+ 'selected_modules': GetSelectedModules(),
+ 'mode': $(':input[name=mode]').val(),
+ 'auth_user': $(':input[name=auth_user]').val(),
+ 'auth_pwd': $(':input[name=auth_pwd]').val(),
+ 'language': $(':input[name=language]').val()
+ },
+ DoLoadDataAsynchronous, 'html');
+// $('#log').ajaxError(
+// function(e, xhr, settings, exception)
+// {
+// bStopAysncProcess = true;
+// alert('Fatal error detected: '+ xhr.responseText);
+// $('#log').append(xhr.responseText);
+// $('#setup').unblock();
+// } );
+ }
+ catch(err)
+ {
+ alert('An exception occured: '+err);
+ }
+ return true; // Continue loading the data
+}
+
var aFilesToLoad = new Array();
var iCounter = 0;
-function DoLoadDataAsynchronous()
+function DoLoadDataAsynchronous(response, status, xhr)
{
+ if (status == 'error')
+ {
+ $('#setup').unblock();
+ return; // An error occurred !
+ }
try
{
// The array aFilesToLoad is populated by this function dynamically written on the server
PopulateDataFilesList();
- iCounter = 0;
- $('#log').html('');
- $('#setup').block({message: 'Loading data...
0%
'});
- $('#progress').progression( {Current:0, Maximum: 100, aBackgroundImg: 'orange-progress.gif', aTextColor: '#000000'} );
- $('#log').ajaxError(
- function(e, xhr, settings, exception)
- {
- alert('Fatal error detected: '+ xhr.responseText);
- $('#log').append(xhr.responseText);
- $('#setup').unblock();
- } );
+ iCurrent = 60;
+ if (aFilesToLoad.length == 0)
+ {
+ $('#progress').progression( {Current: 100} );
+ }
+ else
+ {
+ $('#log').html('');
+ $('#setup').block({message: 'Loading data...
0%
'});
+ $('#progress').progression( {Current: 60, Maximum: 100, aBackgroundImg: 'orange-progress.gif', aTextColor: '#000000'} );
+// $('#log').ajaxError(
+// function(e, xhr, settings, exception)
+// {
+// bStopAysncProcess = true;
+// alert('Fatal error detected: '+ xhr.responseText);
+// $('#log').append(xhr.responseText);
+// $('#setup').unblock();
+// } );
+ }
LoadNextDataFile('', '', '');
}
catch(err)
{
alert('An exception occured: '+err);
}
- return false; // Stop here for now
+ return true; // Continue
}
function LoadNextDataFile(response, status, xhr)
@@ -168,16 +254,17 @@ function LoadNextDataFile(response, status, xhr)
{
sSessionStatus = 'continue';
}
- iPercent = Math.round((100.0 * (1+iCounter)) / aFilesToLoad.length);
+ iPercent = 60+Math.round((40.0 * (1+iCounter)) / aFilesToLoad.length);
sFileName = aFilesToLoad[iCounter];
//alert('Loading file '+sFileName+' ('+iPercent+' %) - '+sSessionStatus);
- $("#progress").progression({ Current: iPercent });
+ $("#progress").progression({ Current: iPercent, Maximum: 100, aBackgroundImg: 'orange-progress.gif', aTextColor: '#000000' });
iCounter++;
- $('#log').load( 'ajax.dataloader.php', { 'file': sFileName, 'percent': iPercent, 'session_status': sSessionStatus }, LoadNextDataFile, 'html');
+ $('#log').load( 'ajax.dataloader.php', { 'operation': 'load_data', 'file': sFileName, 'percent': iPercent, 'session_status': sSessionStatus }, LoadNextDataFile, 'html');
}
else
{
// We're done
+ $("#progress").progression({ Current: 100, Maximum: 100, aBackgroundImg: 'orange-progress.gif', aTextColor: '#000000' });
$('#setup').unblock();
$('#GoToNextStep').submit(); // Use the hidden form to navigate to the next step
}
@@ -187,3 +274,10 @@ function LoadNextDataFile(response, status, xhr)
alert('An exception occurred: '+err);
}
}
+
+function GetSelectedModules()
+{
+ var aModules = new Array();
+ $(':input[name^=module]').each(function() { aModules.push($(this).val()); } );
+ return aModules.join(',');
+}
\ No newline at end of file
diff --git a/setup/setuppage.class.inc.php b/setup/setuppage.class.inc.php
index bf00b0590..061efc871 100644
--- a/setup/setuppage.class.inc.php
+++ b/setup/setuppage.class.inc.php
@@ -25,6 +25,11 @@
require_once(APPROOT."/application/nicewebpage.class.inc.php");
define('INSTALL_LOG_FILE', APPROOT.'/setup.log');
+
+define ('MODULE_ACTION_OPTIONAL', 1);
+define ('MODULE_ACTION_MANDATORY', 2);
+define ('MODULE_ACTION_IMPOSSIBLE', 3);
+
date_default_timezone_set('Europe/Paris');
class SetupWebPage extends NiceWebPage
{
@@ -86,6 +91,11 @@ h2 {
color: #000;
font-size: 14pt;
}
+h3 {
+ color: #1C94C4;
+ font-size: 12pt;
+ font-weight: bold;
+}
.next {
width: 100%;
text-align: right;
@@ -262,7 +272,7 @@ table.formTable {
static $m_aFilesList = array('datamodel', 'webservice', 'dictionary', 'data.struct', 'data.sample');
static $m_sModulePath = null;
- public function SetModulePath($sModulePath)
+ public static function SetModulePath($sModulePath)
{
self::$m_sModulePath = $sModulePath;
}
@@ -297,7 +307,7 @@ table.formTable {
}
}
}
- public function GetModules()
+ public static function GetModules($oP = null)
{
// Order the modules to take into account their inter-dependencies
$aDependencies = array();
@@ -336,7 +346,14 @@ table.formTable {
$sHtml.= "{$aModule['label']} (id: $sId), depends on: ".implode(', ', $aDeps)." ";
}
$sHtml .= "\n";
- $this->warning($sHtml);
+ if (is_object($oP))
+ {
+ $oP->warning($sHtml);
+ }
+ else
+ {
+ self::log_warning($sHtml);
+ }
}
// Return the ordered list, so that the dependencies are met...
$aResult = array();
@@ -346,6 +363,370 @@ table.formTable {
}
return $aResult;
}
-
} // End of class
+
+/**
+ * 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 $bModelOnly boolean Whether or not to allow loading a data model with no corresponding DB
+ * @return none
+ */
+function InitDataModel($sConfigFileName, $bModelOnly = true, $bUseCache = false)
+{
+ require_once(APPROOT.'/core/log.class.inc.php');
+ require_once(APPROOT.'/core/kpi.class.inc.php');
+ require_once(APPROOT.'/core/coreexception.class.inc.php');
+ require_once(APPROOT.'/core/dict.class.inc.php');
+ require_once(APPROOT.'/core/attributedef.class.inc.php');
+ require_once(APPROOT.'/core/filterdef.class.inc.php');
+ require_once(APPROOT.'/core/stimulus.class.inc.php');
+ require_once(APPROOT.'/core/MyHelpers.class.inc.php');
+ require_once(APPROOT.'/core/expression.class.inc.php');
+ require_once(APPROOT.'/core/cmdbsource.class.inc.php');
+ require_once(APPROOT.'/core/sqlquery.class.inc.php');
+ require_once(APPROOT.'/core/dbobject.class.php');
+ require_once(APPROOT.'/core/dbobjectsearch.class.php');
+ require_once(APPROOT.'/core/dbobjectset.class.php');
+ require_once(APPROOT.'/application/cmdbabstract.class.inc.php');
+ require_once(APPROOT.'/core/userrights.class.inc.php');
+ require_once(APPROOT.'/setup/moduleinstallation.class.inc.php');
+ SetupWebPage::log_info("MetaModel::Startup from file '$sConfigFileName' (ModelOnly = $bModelOnly)");
+
+ if ($bUseCache)
+ {
+ // Reset the cache for the first use !
+ $oConfig = new Config($sConfigFileName, false);
+ MetaModel::ResetCache($oConfig);
+ }
+
+ MetaModel::Startup($sConfigFileName, $bModelOnly, $bUseCache);
+}
+
+/**
+ * Search (on the disk) for all defined iTop modules, load them and returns the list (as an array)
+ * of the possible iTop modules to install
+ * @param none
+ * @return Hash A big array moduleID => ModuleData
+ */
+function GetAvailableModules($oP = null)
+{
+ clearstatcache();
+ ListModuleFiles('modules');
+ return SetupWebPage::GetModules($oP);
+}
+
+/**
+ * Analyzes the current installation and the possibilities
+ *
+ * @param $oP SetupWebPage For accessing the list of loaded modules
+ * @param $sDBServer string Name/IP of the DB server
+ * @param $sDBUser username for the DB server connection
+ * @param $sDBPwd password for the DB server connection
+ * @param $sDBName Name of the database instance
+ * @param $sDBPrefix Prefix for the iTop tables in the DB instance
+ * @return hash Array with the following format:
+ * array =>
+ * 'iTop' => array(
+ * 'version_db' => ... (could be empty in case of a fresh install)
+ * 'version_code => ...
+ * )
+ * => array(
+ * 'version_db' => ...
+ * 'version_code' => ...
+ * 'install' => array(
+ * 'flag' => SETUP_NEVER | SETUP_OPTIONAL | SETUP_MANDATORY
+ * 'message' => ...
+ * )
+ * 'uninstall' => array(
+ * 'flag' => SETUP_NEVER | SETUP_OPTIONAL | SETUP_MANDATORY
+ * 'message' => ...
+ * )
+ * 'label' => ...
+ * 'dependencies' => array(, , ...)
+ * 'visible' => true | false
+ * )
+ * )
+ */
+function AnalyzeInstallation($oConfig)
+{
+ $aRes = array(
+ 'iTop' => array(
+ 'version_db' => '',
+ 'version_code' => ITOP_VERSION.'.'.ITOP_REVISION,
+ )
+ );
+
+ $aModules = GetAvailableModules();
+ foreach($aModules as $sModuleId => $aModuleInfo)
+ {
+ list($sModuleName, $sModuleVersion) = GetModuleName($sModuleId);
+
+ $sModuleAppVersion = $aModuleInfo['itop_version'];
+ $aModuleInfo['version_db'] = '';
+ $aModuleInfo['version_code'] = $sModuleVersion;
+
+ if (!in_array($sModuleAppVersion, array('1.0.0', '1.0.1', '1.0.2')))
+ {
+ // This module is NOT compatible with the current version
+ $aModuleInfo['install'] = array(
+ 'flag' => MODULE_ACTION_IMPOSSIBLE,
+ 'message' => 'the module is not compatible with the current version of the application'
+ );
+ }
+ elseif ($aModuleInfo['mandatory'])
+ {
+ $aModuleInfo['install'] = array(
+ 'flag' => MODULE_ACTION_MANDATORY,
+ 'message' => 'the module is part of the application'
+ );
+ }
+ else
+ {
+ $aModuleInfo['install'] = array(
+ 'flag' => MODULE_ACTION_OPTIONAL,
+ 'message' => ''
+ );
+ }
+ $aRes[$sModuleName] = $aModuleInfo;
+ }
+
+ try
+ {
+ CMDBSource::Init($oConfig->GetDBHost(), $oConfig->GetDBUser(), $oConfig->GetDBPwd(), $oConfig->GetDBName());
+ $aSelectInstall = CMDBSource::QueryToArray("SELECT * FROM ".$oConfig->GetDBSubname()."priv_module_install");
+ }
+ catch (MySQLException $e)
+ {
+ // No database or eroneous information
+ $aSelectInstall = array();
+ }
+
+ // Build the list of installed module (get the latest installation)
+ //
+ $aInstallByModule = array(); // array of => array ('installed' => timestamp, 'version' => )
+ foreach ($aSelectInstall as $aInstall)
+ {
+ //$aInstall['comment']; // unsused
+ //$aInstall['parent_id']; // unsused
+ $iInstalled = strtotime($aInstall['installed']);
+ $sModuleName = $aInstall['name'];
+ $sModuleVersion = $aInstall['version'];
+
+ if ($sModuleName == 'itop')
+ {
+ $aRes['iTop']['version_db'] = $sModuleVersion;
+ continue;
+ }
+
+ if (array_key_exists($sModuleName, $aInstallByModule))
+ {
+ if ($iInstalled < $aInstallByModule[$sModuleName]['installed'])
+ {
+ continue;
+ }
+ }
+ $aInstallByModule[$sModuleName]['installed'] = $iInstalled;
+ $aInstallByModule[$sModuleName]['version'] = $sModuleVersion;
+ }
+
+ // Adjust the list of proposed modules
+ //
+ foreach ($aInstallByModule as $sModuleName => $aModuleDB)
+ {
+ if (!array_key_exists($sModuleName, $aRes))
+ {
+ // A module was installed, it is not proposed in the new build... skip
+ continue;
+ }
+ $aRes[$sModuleName]['version_db'] = $aModuleDB['version'];
+
+ if ($aRes[$sModuleName]['install']['flag'] == MODULE_ACTION_MANDATORY)
+ {
+ $aRes[$sModuleName]['uninstall'] = array(
+ 'flag' => MODULE_ACTION_IMPOSSIBLE,
+ 'message' => 'the module is part of the application'
+ );
+ }
+ else
+ {
+ $aRes[$sModuleName]['uninstall'] = array(
+ 'flag' => MODULE_ACTION_OPTIONAL,
+ 'message' => ''
+ );
+ }
+ }
+
+ return $aRes;
+}
+
+
+/**
+ * Helper function to interpret the name of a module
+ * @param $sModuleId string Identifier of the module, in the form 'name/version'
+ * @return array(name, version)
+ */
+function GetModuleName($sModuleId)
+{
+ if (preg_match('!^(.*)/(.*)$!', $sModuleId, $aMatches))
+ {
+ $sName = $aMatches[1];
+ $sVersion = $aMatches[2];
+ }
+ else
+ {
+ $sName = $sModuleId;
+ $sVersion = "";
+ }
+ return array($sName, $sVersion);
+}
+/**
+ * Helper function to create the database structure
+ * @return boolean true on success, false otherwise
+ */
+function CreateDatabaseStructure(Config $oConfig, $aSelectedModules, $sMode)
+{
+ if (strlen($oConfig->GetDBSubname()) > 0)
+ {
+ SetupWebPage::log_info("Creating the structure in '".$oConfig->GetDBName()."' (table names prefixed by '".$oConfig->GetDBSubname()."').");
+ }
+ else
+ {
+ SetupWebPage::log_info("Creating the structure in '".$oConfig->GetDBSubname()."'.");
+ }
+
+ //MetaModel::CheckDefinitions();
+ if ($sMode == 'install')
+ {
+ if (!MetaModel::DBExists(/* bMustBeComplete */ false))
+ {
+ MetaModel::DBCreate();
+ SetupWebPage::log_ok("Database structure successfully created.");
+ }
+ else
+ {
+ if (strlen($oConfig->GetDBSubname()) > 0)
+ {
+ throw new Exception("Error: found iTop tables into the database '".$oConfig->GetDBName()."' (prefix: '".$oConfig->GetDBSubname()."'). Please, try selecting another database instance or specify another prefix to prevent conflicting table names.");
+ }
+ else
+ {
+ throw new Exception("Error: found iTop tables into the database '".$oConfig->GetDBName()."'. Please, try selecting another database instance or specify a prefix to prevent conflicting table names.");
+ }
+ }
+ }
+ else
+ {
+ if (MetaModel::DBExists(/* bMustBeComplete */ false))
+ {
+ MetaModel::DBCreate();
+ SetupWebPage::log_ok("Database structure successfully created.");
+ }
+ else
+ {
+ if (strlen($oConfig->GetDBSubname()) > 0)
+ {
+ throw new Exception("Error: No previous instance of iTop found into the database '".$oConfig->GetDBName()."' (prefix: '".$oConfig->GetDBSubname()."'). Please, try selecting another database instance.");
+ }
+ else
+ {
+ throw new Exception("Error: No previous instance of iTop found into the database '".$oConfig->GetDBName()."'. Please, try selecting another database instance.");
+ }
+ }
+ }
+ return true;
+}
+
+function RecordInstallation(Config $oConfig, $aSelectedModules)
+{
+ // Record main installation
+ $oInstallRec = new ModuleInstallation();
+ $oInstallRec->Set('name', 'itop');
+ $oInstallRec->Set('version', ITOP_VERSION.'.'.ITOP_REVISION);
+ $oInstallRec->Set('comment', "Done by the setup program\nBuilt on ".ITOP_BUILD_DATE);
+ $oInstallRec->Set('parent_id', 0); // root module
+ $iMainItopRecord = $oInstallRec->DBInsertNoReload();
+
+ // Record installed modules
+ //
+ $aAvailableModules = AnalyzeInstallation($oConfig);
+ foreach($aSelectedModules as $sModuleId)
+ {
+ $aModuleData = $aAvailableModules[$sModuleId];
+ $sName = $sModuleId;
+ $sVersion = $aModuleData['version_code'];
+ $aComments = array();
+ $aComments[] = 'Done by the setup program';
+ if ($aModuleData['mandatory'])
+ {
+ $aComments[] = 'Mandatory';
+ }
+ else
+ {
+ $aComments[] = 'Optional';
+ }
+ if ($aModuleData['visible'])
+ {
+ $aComments[] = 'Visible (during the setup)';
+ }
+ else
+ {
+ $aComments[] = 'Hidden (selected automatically)';
+ }
+ foreach ($aModuleData['dependencies'] as $sDependOn)
+ {
+ $aComments[] = "Depends on module: $sDependOn";
+ }
+ $sComment = implode("\n", $aComments);
+
+ $oInstallRec = new ModuleInstallation();
+ $oInstallRec->Set('name', $sName);
+ $oInstallRec->Set('version', $sVersion);
+ $oInstallRec->Set('comment', $sComment);
+ $oInstallRec->Set('parent_id', $iMainItopRecord);
+ $oInstallRec->DBInsertNoReload();
+ }
+ // Database is created, installation has been tracked into it
+ return true;
+}
+
+function ListModuleFiles($sRelDir)
+{
+ $sDirectory = APPROOT.'/'.$sRelDir;
+ //echo "$sDirectory
\n";
+ if ($hDir = opendir($sDirectory))
+ {
+ // This is the correct way to loop over the directory. (according to the documentation)
+ while (($sFile = readdir($hDir)) !== false)
+ {
+ $aMatches = array();
+ if (is_dir($sDirectory.'/'.$sFile))
+ {
+ if (($sFile != '.') && ($sFile != '..') && ($sFile != '.svn'))
+ {
+ ListModuleFiles($sRelDir.'/'.$sFile);
+ }
+ }
+ else if (preg_match('/^module\.(.*).php$/i', $sFile, $aMatches))
+ {
+ SetupWebPage::SetModulePath($sRelDir);
+ try
+ {
+ //echo "Loading: $sDirectory/$sFile...
\n";
+ require_once($sDirectory.'/'.$sFile);
+ //echo "Done.
\n";
+ }
+ catch(Exception $e)
+ {
+ // Continue...
+ }
+ }
+ }
+ closedir($hDir);
+ }
+ else
+ {
+ throw new Exception("Data directory (".$sDirectory.") not found or not readable.");
+ }
+}
?>