New implementation of the setup:

- All actions are performed asynchronously at the end of the setup
- Supports upgrading and reinstalling (to add modules)

SVN:trunk[1157]
This commit is contained in:
Denis Flaven
2011-03-30 17:28:01 +00:00
parent eb3d226b3d
commit e3ac7067f7
7 changed files with 2179 additions and 1400 deletions

View File

@@ -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);

View File

@@ -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)
{

View File

@@ -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 "<p>An error happened while loading the data</p>\n";
echo "<p>An error happened while processing the installation:</p>\n";
echo '<p>'.$e."</p>\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());
}
}
?>

File diff suppressed because it is too large Load Diff

View File

@@ -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)
{
}
}

View File

@@ -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: '<p>Updating DB schema...<br/><div id=\"progress\">0%</div></p>'});
$('#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: '<p>Updating Profiles...<br/><div id=\"progress\">0%</div></p>'});
$('#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: '<p>Loading data...<br/><div id=\"progress\">0%</div></p>'});
$('#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: '<p>Loading data...<br/><div id=\"progress\">0%</div></p>'});
$('#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(',');
}

View File

@@ -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.= "<li>{$aModule['label']} (id: $sId), depends on: ".implode(', ', $aDeps)."</li>";
}
$sHtml .= "</ul>\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 => ...
* )
* <module_name> => 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(<module1>, <module2>, ...)
* '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 <module> => array ('installed' => timestamp, 'version' => <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 "<p>$sDirectory</p>\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 "<p>Loading: $sDirectory/$sFile...</p>\n";
require_once($sDirectory.'/'.$sFile);
//echo "<p>Done.</p>\n";
}
catch(Exception $e)
{
// Continue...
}
}
}
closedir($hDir);
}
else
{
throw new Exception("Data directory (".$sDirectory.") not found or not readable.");
}
}
?>