Преглед на файлове

Setup based on either compiled modules or xml datamodel files (or both).
SetupWebPage is an alias for ModuleDiscovery and the module files should be updated progressively to invoke ModuleDiscovery::AddModule() instead.
The implementation of the module still assumes they are in the directory 'modules'... this has to be changed later to ensure the distinction between the source modules and the executed modules

git-svn-id: http://svn.code.sf.net/p/itop/code/trunk@1758 a333f486-631f-4898-b8df-5754b55c2be0

romainq преди 13 години
родител
ревизия
8b6fec98fb

+ 1 - 1
application/loginwebpage.class.inc.php

@@ -433,7 +433,7 @@ EOF
 		if ($bMustBeAdmin && !UserRights::IsAdministrator())
 		{	
 			require_once(APPROOT.'/setup/setuppage.class.inc.php');
-			$oP = new SetupWebPage(Dict::S('UI:PageTitle:FatalError'));
+			$oP = new SetupPage(Dict::S('UI:PageTitle:FatalError'));
 			$oP->add("<h1>".Dict::S('UI:Login:Error:AccessAdmin')."</h1>\n");	
 			$oP->p("<a href=\"".utils::GetAbsoluteUrlAppRoot()."pages/logoff.php\">".Dict::S('UI:LogOffMenu')."</a>");
 			$oP->output();

+ 2 - 2
core/config.class.inc.php

@@ -551,8 +551,8 @@ class Config
 	protected $m_bLogNotification;
 	protected $m_bLogIssue;
 	protected $m_bLogWebService;
-	protected $m_bLogKpiDuration; // private setting
-	protected $m_bLogKpiMemory; // private setting
+	protected $m_bLogKPIDuration; // private setting
+	protected $m_bLogKPIMemory; // private setting
 	protected $m_bDebugQueries; // private setting
 	protected $m_bQueryCacheEnabled; // private setting
 

+ 24 - 11
core/metamodel.class.php

@@ -825,7 +825,11 @@ if (!array_key_exists($sAttCode, self::$m_aAttribDefs[$sClass]))
 
 	final static public function GetClassFilterDef($sClass, $sFilterCode)
 	{
-		self::_check_subclass($sClass);	
+		self::_check_subclass($sClass);
+		if (!array_key_exists($sFilterCode, self::$m_aFilterDefs[$sClass]))
+		{
+			throw new CoreException("Unknown filter code '$sFilterCode' for class '$sClass'");
+		}
 		return self::$m_aFilterDefs[$sClass][$sFilterCode];
 	}
 
@@ -4028,11 +4032,19 @@ if (!array_key_exists($sAttCode, self::$m_aAttribDefs[$sClass]))
 		}
 	}
 
-	public static function Startup($sConfigFile, $bModelOnly = false, $bAllowCache = true, $bTraceSourceFiles = false)
+	public static function Startup($config, $bModelOnly = false, $bAllowCache = true, $bTraceSourceFiles = false)
 	{
 		self::$m_bTraceSourceFiles = $bTraceSourceFiles;
 
-		self::LoadConfig($sConfigFile, $bAllowCache);
+		// $config can be either a filename, or a Configuration object (volatile!)
+		if ($config instanceof Config)
+		{
+			self::LoadConfig($config, $bAllowCache);
+		}
+		else
+		{
+			self::LoadConfig(new Config($config), $bAllowCache);
+		}
 
 		if ($bModelOnly) return;
 
@@ -4054,9 +4066,9 @@ if (!array_key_exists($sAttCode, self::$m_aAttribDefs[$sClass]))
 		}
 	}
 
-	public static function LoadConfig($sConfigFile, $bAllowCache = false)
+	public static function LoadConfig($oConfiguration, $bAllowCache = false)
 	{
-		self::$m_oConfig = new Config($sConfigFile);
+		self::$m_oConfig = $oConfiguration;
 
 		// Set log ASAP
 		if (self::$m_oConfig->GetLogGlobal())
@@ -4119,7 +4131,7 @@ if (!array_key_exists($sAttCode, self::$m_aAttribDefs[$sClass]))
 		{
 			foreach (self::$m_oConfig->GetDictionaries() as $sModule => $sToInclude)
 			{
-				self::IncludeModule($sConfigFile, 'dictionaries', $sToInclude);
+				self::IncludeModule('dictionaries', $sToInclude);
 			}
 			if (self::$m_bUseAPCCache)
 			{
@@ -4135,19 +4147,19 @@ if (!array_key_exists($sAttCode, self::$m_aAttribDefs[$sClass]))
 
 		foreach (self::$m_oConfig->GetAppModules() as $sModule => $sToInclude)
 		{
-			self::IncludeModule($sConfigFile, 'application', $sToInclude);
+			self::IncludeModule('application', $sToInclude);
 		}
 		foreach (self::$m_oConfig->GetDataModels() as $sModule => $sToInclude)
 		{
-			self::IncludeModule($sConfigFile, 'business', $sToInclude);
+			self::IncludeModule('business', $sToInclude);
 		}
 		foreach (self::$m_oConfig->GetWebServiceCategories() as $sModule => $sToInclude)
 		{
-			self::IncludeModule($sConfigFile, 'webservice', $sToInclude);
+			self::IncludeModule('webservice', $sToInclude);
 		}
 		foreach (self::$m_oConfig->GetAddons() as $sModule => $sToInclude)
 		{
-			self::IncludeModule($sConfigFile, 'addons', $sToInclude);
+			self::IncludeModule('addons', $sToInclude);
 		}
 
 		$sServer = self::$m_oConfig->GetDBHost();
@@ -4249,7 +4261,7 @@ if (!array_key_exists($sAttCode, self::$m_aAttribDefs[$sClass]))
 
 	protected static $m_aExtensionClasses = array();
 
-	protected static function IncludeModule($sConfigFile, $sModuleType, $sToInclude)
+	protected static function IncludeModule($sModuleType, $sToInclude)
 	{
 		$sFirstChar = substr($sToInclude, 0, 1);
 		$sSecondChar = substr($sToInclude, 1, 1);
@@ -4274,6 +4286,7 @@ if (!array_key_exists($sAttCode, self::$m_aAttribDefs[$sClass]))
 		}
 		if (!file_exists($sFile))
 		{
+			$sConfigFile = self::$m_oConfig->GetLoadedFile();
 			throw new CoreException('Wrong filename in configuration file', array('file' => $sConfigFile, 'module' => $sModuleType, 'filename' => $sFile));
 		}
 		require_once($sFile);

+ 3 - 3
modules/itop-attachments/module.attachments.php

@@ -98,12 +98,12 @@ class AttachmentInstaller extends ModuleInstallerAPI
 		//    get the org_id from the container object 
 		//
 		// Prerequisite: change null into 0 (workaround to the fact that we cannot use IS NULL in OQL)
-		SetupWebPage::log_info("Initializing attachment/item_org_id - null to zero"); 
+		SetupPage::log_info("Initializing attachment/item_org_id - null to zero"); 
 		$sTableName = MetaModel::DBGetTable('Attachment');
 		$sRepair = "UPDATE `$sTableName` SET `item_org_id` = 0 WHERE `item_org_id` IS NULL";
 		CMDBSource::Query($sRepair);
 
-		SetupWebPage::log_info("Initializing attachment/item_org_id - zero to the container");
+		SetupPage::log_info("Initializing attachment/item_org_id - zero to the container");
 		$oSearch = DBObjectSearch::FromOQL("SELECT Attachment WHERE item_org_id = 0");
 		$oSet = new DBObjectSet($oSearch);
 		$iUpdated = 0;
@@ -117,7 +117,7 @@ class AttachmentInstaller extends ModuleInstallerAPI
 			}
 		}
 
-		SetupWebPage::log_info("Initializing attachment/item_org_id - $iUpdated records have been adjusted"); 
+		SetupPage::log_info("Initializing attachment/item_org_id - $iUpdated records have been adjusted"); 
 	}
 }
 

+ 1 - 1
modules/itop-change-mgmt-1.0.0/module.itop-change-mgmt.php

@@ -85,7 +85,7 @@ class ChangeManagementInstaller extends ModuleInstallerAPI
 
 		if (CMDBSource::IsField($sSourceTable, $sField) && CMDBSource::IsField($sTargetTable, $sField) && CMDBSource::IsField($sSourceTable, $sSourceKeyField) && CMDBSource::IsField($sTargetTable, $sTargetKeyField))
 		{
-			SetupWebPage::log_info("Issue #464 - Copying change/start_date into ticket/start_date"); 
+			SetupPage::log_info("Issue #464 - Copying change/start_date into ticket/start_date"); 
 			$sRepair = "UPDATE `$sTargetTable`, `$sSourceTable` SET `$sTargetTable`.`$sField` = `$sSourceTable`.`$sField` WHERE `$sTargetTable`.`$sField` IS NULL AND`$sTargetTable`.`$sTargetKeyField` = `$sSourceTable`.`$sSourceKeyField`";
 			CMDBSource::Query($sRepair);
 		}

+ 0 - 8
modules/itop-config-mgmt-1.0.0/model.itop-config-mgmt.php

@@ -1599,14 +1599,6 @@ class lnkProcessToSolution extends cmdbAbstractObject
 // Create the top-level group. fRank = 1, means it will be inserted after the group '0', which is usually 'Welcome'
 
 
-// Note (RQ) :
-// After 1.0.1, the welcome page and menus have been removed from the application
-// and put into a separate module "itop-welcome-itil"
-// Until we develop a migration utility, and as would like to preserve the
-// capability to upgrade iTop without any manual operation, we have decided to
-// implement this dirty workaround that makes it...
-require_once(APPROOT.'modules/itop-welcome-itil/model.itop-welcome-itil.php');
-
 // Starting with iTop 1.2 you can restrict the list of organizations displayed in the drop-down list
 // by specifying a query as shown below. Note that this is NOT a security settings, since the
 // choice 'All Organizations' will always be available in the menu

+ 2 - 2
pages/UI.php

@@ -2188,7 +2188,7 @@ EOF
 catch(CoreException $e)
 {
 	require_once(APPROOT.'/setup/setuppage.class.inc.php');
-	$oP = new SetupWebPage(Dict::S('UI:PageTitle:FatalError'));
+	$oP = new SetupPage(Dict::S('UI:PageTitle:FatalError'));
 	if ($e instanceof SecurityException)
 	{
 		$oP->add("<h1>".Dict::S('UI:SystemIntrusion')."</h1>\n");
@@ -2231,7 +2231,7 @@ catch(CoreException $e)
 catch(Exception $e)
 {
 	require_once(APPROOT.'/setup/setuppage.class.inc.php');
-	$oP = new SetupWebPage(Dict::S('UI:PageTitle:FatalError'));
+	$oP = new SetupPage(Dict::S('UI:PageTitle:FatalError'));
 	$oP->add("<h1>".Dict::S('UI:FatalErrorMessage')."</h1>\n");	
 	$oP->error(Dict::Format('UI:Error_Details', $e->getMessage()));	
 	$oP->output();

+ 2 - 2
pages/audit.php

@@ -269,7 +269,7 @@ try
 catch(CoreException $e)
 {
 	require_once(APPROOT.'/setup/setuppage.class.inc.php');
-	$oP = new SetupWebPage(Dict::S('UI:PageTitle:FatalError'));
+	$oP = new SetupPage(Dict::S('UI:PageTitle:FatalError'));
 	$oP->add("<h1>".Dict::S('UI:FatalErrorMessage')."</h1>\n");	
 	$oP->error(Dict::Format('UI:Error_Details', $e->getHtmlDesc()));	
 	$oP->output();
@@ -298,7 +298,7 @@ catch(CoreException $e)
 catch(Exception $e)
 {
 	require_once(APPROOT.'/setup/setuppage.class.inc.php');
-	$oP = new SetupWebPage(Dict::S('UI:PageTitle:FatalError'));
+	$oP = new SetupPage(Dict::S('UI:PageTitle:FatalError'));
 	$oP->add("<h1>".Dict::S('UI:FatalErrorMessage')."</h1>\n");	
 	$oP->error(Dict::Format('UI:Error_Details', $e->getMessage()));	
 	$oP->output();

+ 4 - 4
pages/csvimport.php

@@ -1438,10 +1438,10 @@ EOF
 	
 	$oPage->output();
 }
-catch(xxxxxxxCoreException $e)
+catch(CoreException $e)
 {
 	require_once(APPROOT.'/setup/setuppage.class.inc.php');
-	$oP = new SetupWebPage(Dict::S('UI:PageTitle:FatalError'));
+	$oP = new SetupPage(Dict::S('UI:PageTitle:FatalError'));
 	$oP->add("<h1>".Dict::S('UI:FatalErrorMessage')."</h1>\n");	
 	$oP->error(Dict::Format('UI:Error_Details', $e->getHtmlDesc()));	
 	$oP->output();
@@ -1467,10 +1467,10 @@ catch(xxxxxxxCoreException $e)
 	// For debugging only
 	//throw $e;
 }
-catch(xxxxxException $e)
+catch(Exception $e)
 {
 	require_once(APPROOT.'/setup/setuppage.class.inc.php');
-	$oP = new SetupWebPage(Dict::S('UI:PageTitle:FatalError'));
+	$oP = new SetupPage(Dict::S('UI:PageTitle:FatalError'));
 	$oP->add("<h1>".Dict::S('UI:FatalErrorMessage')."</h1>\n");	
 	$oP->error(Dict::Format('UI:Error_Details', $e->getMessage()));	
 	$oP->output();

+ 2 - 2
pages/preferences.php

@@ -157,7 +157,7 @@ try
 catch(CoreException $e)
 {
 	require_once(APPROOT.'/setup/setuppage.class.inc.php');
-	$oP = new SetupWebPage(Dict::S('UI:PageTitle:FatalError'));
+	$oP = new SetupPage(Dict::S('UI:PageTitle:FatalError'));
 	$oP->add("<h1>".Dict::S('UI:FatalErrorMessage')."</h1>\n");	
 	$oP->error(Dict::Format('UI:Error_Details', $e->getHtmlDesc()));	
 	$oP->output();
@@ -186,7 +186,7 @@ catch(CoreException $e)
 catch(Exception $e)
 {
 	require_once(APPROOT.'/setup/setuppage.class.inc.php');
-	$oP = new SetupWebPage(Dict::S('UI:PageTitle:FatalError'));
+	$oP = new SetupPage(Dict::S('UI:PageTitle:FatalError'));
 	$oP->add("<h1>".Dict::S('UI:FatalErrorMessage')."</h1>\n");	
 	$oP->error(Dict::Format('UI:Error_Details', $e->getMessage()));	
 	$oP->output();

+ 2 - 2
portal/index.php

@@ -816,7 +816,7 @@ try
 catch(CoreException $e)
 {
 	require_once(APPROOT.'/setup/setuppage.class.inc.php');
-	$oP = new SetupWebPage(Dict::S('UI:PageTitle:FatalError'));
+	$oP = new SetupPage(Dict::S('UI:PageTitle:FatalError'));
 	$oP->add("<h1>".Dict::S('UI:FatalErrorMessage')."</h1>\n");	
 	$oP->error(Dict::Format('UI:Error_Details', $e->getHtmlDesc()));	
 	$oP->output();
@@ -852,7 +852,7 @@ catch(CoreException $e)
 catch(Exception $e)
 {
 	require_once(APPROOT.'/setup/setuppage.class.inc.php');
-	$oP = new SetupWebPage(Dict::S('UI:PageTitle:FatalError'));
+	$oP = new SetupPage(Dict::S('UI:PageTitle:FatalError'));
 	$oP->add("<h1>".Dict::S('UI:FatalErrorMessage')."</h1>\n");	
 	$oP->error(Dict::Format('UI:Error_Details', $e->getMessage()));	
 	$oP->output();

+ 138 - 33
setup/ajax.dataloader.php

@@ -26,7 +26,7 @@
 /**
  * This page is called to perform "asynchronously" the setup actions
  * parameters
- * 'operation': one of 'update_db_schema', 'after_db_creation', 'file'
+ * 'operation': one of 'compile_data_model', 'update_db_schema', 'after_db_creation', 'file'
  * 
  * if 'operation' == 'update_db_schema': 
  * 'mode': install | upgrade
@@ -51,7 +51,7 @@ 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 with the data load
-	SetupWebPage::log_info("No memory limit has been defined in this instance of PHP");		
+	SetupPage::log_info("No memory limit has been defined in this instance of PHP");		
 }
 else
 {
@@ -62,11 +62,11 @@ else
 	{
 		if (ini_set('memory_limit', SAFE_MINIMUM_MEMORY) === FALSE)
 		{
-			SetupWebPage::log_error("memory_limit is too small: $iMemoryLimit and can not be increased by the script itself.");		
+			SetupPage::log_error("memory_limit is too small: $iMemoryLimit and can not be increased by the script itself.");		
 		}
 		else
 		{
-			SetupWebPage::log_info("memory_limit increased from $iMemoryLimit to ".SAFE_MINIMUM_MEMORY.".");		
+			SetupPage::log_info("memory_limit increased from $iMemoryLimit to ".SAFE_MINIMUM_MEMORY.".");		
 		}
 	}
 
@@ -84,7 +84,7 @@ function FatalErrorCatcher($sOutput)
 		}
 		$sOutput = "$errors\n";
 		// Logging to a file does not work if the whole memory is exhausted...		
-		//SetupWebPage::log_error("Fatal error - in $__FILE__ , $errors");
+		//SetupPage::log_error("Fatal error - in $__FILE__ , $errors");
 	}
 	return $sOutput;
 }
@@ -95,7 +95,7 @@ function FatalErrorCatcher($sOutput)
  */
 function CreateAdminAccount(Config $oConfig, $sAdminUser, $sAdminPwd, $sLanguage)
 {
-	SetupWebPage::log_info('CreateAdminAccount');
+	SetupPage::log_info('CreateAdminAccount');
 
 	if (UserRights::CreateAdministrator($sAdminUser, $sAdminPwd, $sLanguage))
 	{
@@ -136,49 +136,137 @@ try
 {
 	switch($sOperation)
 	{
+		//////////////////////////////
+		//
+		case 'compile_data_model':
+		//
+		SetupPage::log_info("Compiling data model.");
+
+		require_once(APPROOT.'designer/modulediscovery.class.inc.php');
+		require_once(APPROOT.'designer/modelfactory.inc.class.php');
+		require_once(APPROOT.'designer/compiler.inc.class.php');
+
+		$sSelectedModules = Utils::ReadParam('selected_modules', '', false, 'raw_data');
+		$aSelectedModules = explode(',', $sSelectedModules);
+
+		$sWorkspace = Utils::ReadParam('workspace_dir', '', false, 'raw_data');
+		$sSourceDir = Utils::ReadParam('source_dir', '', false, 'raw_data');
+		$sTargetDir = Utils::ReadParam('target_dir', '', false, 'raw_data');
+		if (empty($sSourceDir) || empty($sTargetDir))
+		{
+			throw new Exception("missing parameter source_dir and/or target_dir");
+		}		
+
+		$sSourcePath = APPROOT.$sSourceDir;
+		$sTargetPath = APPROOT.$sTargetDir;
+		if (!is_dir($sSourcePath))
+		{
+			throw new Exception("Failed to find the source directory '$sSourcePath', please check the rights of the web server");
+		}		
+		if (!is_dir($sTargetPath) && !mkdir($sTargetPath))
+		{
+			throw new Exception("Failed to create directory '$sTargetPath', please check the rights of the web server");
+		}		
+		// owner:rwx user/group:rx
+		chmod($sTargetPath, 0755);
+
+		$oFactory = new ModelFactory($sSourcePath);
+		$aModules = $oFactory->FindModules();
+
+		foreach($aModules as $foo => $oModule)
+		{
+			$sModule = $oModule->GetName();
+			if (in_array($sModule, $aSelectedModules))
+			{
+				$oFactory->LoadModule($oModule);
+			}
+		}
+		if (strlen($sWorkspace) > 0)
+		{
+			$oWorkspace = new MFWorkspace(APPROOT.$sWorkspace);
+			if (file_exists($oWorkspace->GetWorkspacePath()))
+			{
+				$oFactory->LoadModule($oWorkspace);
+			}
+		}
+		//$oFactory->Dump();
+		
+		$oMFCompiler = new MFCompiler($oFactory, $sSourcePath);
+		$oMFCompiler->Compile($sTargetPath);
+		SetupPage::log_info("Data model successfully compiled to '$sTargetPath'.");
+		break;
 		
+		//////////////////////////////
+		//
 		case 'update_db_schema':
-		SetupWebPage::log_info("Update Database Schema.");
-		InitDataModel(TMP_CONFIG_FILE, true);  // load data model and connect to the database
+		//
+		SetupPage::log_info("Update Database Schema.");
+
+		$oConfig = new Config(TMP_CONFIG_FILE, false /* Don't try to load it */);
+
+		$aParamValues = array(
+			'db_server' => utils::ReadParam('db_server', '', false, 'raw_data'),
+			'db_user' => utils::ReadParam('db_user', '', false, 'raw_data'),
+			'db_pwd' => utils::ReadParam('db_pwd', '', false, 'raw_data'),
+			'db_name' => utils::ReadParam('db_name', '', false, 'raw_data'),
+			'new_db_name' => utils::ReadParam('new_db_name', '', false, 'raw_data'),
+			'db_prefix' => utils::ReadParam('db_prefix', '', false, 'raw_data')
+		);
+		$sModuleDir = Utils::ReadParam('modules_dir', '');
+		UpdateConfigSettings($oConfig, $aParamValues, $sModuleDir);
+
+		InitDataModel($oConfig, true);  // load data model only
+
 		$sMode = Utils::ReadParam('mode', 'install');
-		$sSelectedModules = Utils::ReadParam('selected_modules', '', false, 'raw_data');
-		$aSelectedModules = explode(',', $sSelectedModules);
-		if(!CreateDatabaseStructure(MetaModel::GetConfig(), $aSelectedModules, $sMode))
+		if(!CreateDatabaseStructure(MetaModel::GetConfig(), $sMode))
 		{
 			throw(new Exception("Failed to create/upgrade the database structure"));		
 		}
-		SetupWebPage::log_info("Database Schema Successfully Updated.");
+		SetupPage::log_info("Database Schema Successfully Updated.");
 		break;
 		
+		//////////////////////////////
+		//
 		case 'after_db_create':
-		SetupWebPage::log_info('After Database Creation');
+		//
+		SetupPage::log_info('After Database Creation');
+
+		$oConfig = new Config(TMP_CONFIG_FILE, false /* Don't try to load it */);
+
+		$aParamValues = array(
+			'db_server' => utils::ReadParam('db_server', '', false, 'raw_data'),
+			'db_user' => utils::ReadParam('db_user', '', false, 'raw_data'),
+			'db_pwd' => utils::ReadParam('db_pwd', '', false, 'raw_data'),
+			'db_name' => utils::ReadParam('db_name', '', false, 'raw_data'),
+			'new_db_name' => utils::ReadParam('new_db_name', '', false, 'raw_data'),
+			'db_prefix' => utils::ReadParam('db_prefix', '', false, 'raw_data')
+		);
+		$sModuleDir = Utils::ReadParam('modules_dir', '');
+		UpdateConfigSettings($oConfig, $aParamValues, $sModuleDir);
+
+		InitDataModel($oConfig, false);  // load data model and connect to the database
+
 		$sMode = Utils::ReadParam('mode', 'install');
 		$sSelectedModules = Utils::ReadParam('selected_modules', '', false, 'raw_data');
 		$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());
-	
-		$aStructureDataFiles = array();
-		$aSampleDataFiles = array();
-	
+		//
+		$aAvailableModules = AnalyzeInstallation(MetaModel::GetConfig(), $sModuleDir);
 		foreach($aAvailableModules as $sModuleId => $aModule)
 		{
 			if (($sModuleId != ROOT_MODULE) && 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']})");
+				SetupPage::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']));								
 			}
 		}
 
-
-		if (!RecordInstallation(MetaModel::GetConfig(), $aSelectedModules))
+		if (!RecordInstallation($oConfig, $aSelectedModules, $sModuleDir))
 		{
 			throw(new Exception("Failed to record the installation information"));
 		}
@@ -195,22 +283,39 @@ try
 			}
 			else
 			{
-				SetupWebPage::log_info("Administrator account '$sAdminUser' created.");
+				SetupPage::log_info("Administrator account '$sAdminUser' created.");
 			}
 		}
 		break;
 		
+		//////////////////////////////
+		//
 		case 'load_data': // Load data files
+		//
 		$sFileName = Utils::ReadParam('file', '', false, 'raw_data');
 		$sSessionStatus = Utils::ReadParam('session_status', '');
 		$iPercent = (integer)Utils::ReadParam('percent', 0);
-		SetupWebPage::log_info("Loading file: $sFileName");
+		SetupPage::log_info("Loading file: $sFileName");
 		if (empty($sFileName) || !file_exists($sFileName))
 		{
 			throw(new Exception("File $sFileName does not exist"));
 		}
-		
-		InitDataModel(TMP_CONFIG_FILE, false);  // When called by the wizard, the final config is not yet there
+
+		$oConfig = new Config(TMP_CONFIG_FILE, false /* Don't try to load it */);
+
+		$aParamValues = array(
+			'db_server' => utils::ReadParam('db_server', '', false, 'raw_data'),
+			'db_user' => utils::ReadParam('db_user', '', false, 'raw_data'),
+			'db_pwd' => utils::ReadParam('db_pwd', '', false, 'raw_data'),
+			'db_name' => utils::ReadParam('db_name', '', false, 'raw_data'),
+			'new_db_name' => utils::ReadParam('new_db_name', '', false, 'raw_data'),
+			'db_prefix' => utils::ReadParam('db_prefix', '', false, 'raw_data')
+		);
+		$sModuleDir = Utils::ReadParam('modules_dir', '');
+		UpdateConfigSettings($oConfig, $aParamValues, $sModuleDir);
+
+		InitDataModel($oConfig, false);  // load data model and connect to the database
+
 		$oDataLoader = new XMLDataLoader(); 
 		if ($sSessionStatus == 'start')
 		{
@@ -218,18 +323,18 @@ try
 			$oChange->Set("date", time());
 			$oChange->Set("userinfo", "Initialization");
 			$iChangeId = $oChange->DBInsert();
-			SetupWebPage::log_info("starting data load session");
+			SetupPage::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);
+		SetupPage::log_info($sResult);
 	
 		if ($sSessionStatus == 'end')
 		{
 		    $oDataLoader->EndSession();
-		    SetupWebPage::log_info("ending data load session");
+		    SetupPage::log_info("ending data load session");
 		}
 		break;
 		
@@ -242,18 +347,18 @@ catch(Exception $e)
 	header("HTTP/1.0 500 Internal server error.");
 	echo "<p>An error happened while processing the installation:</p>\n";
 	echo '<p>'.$e."</p>\n";
-	SetupWebPage::log_error("An error happened while processing the installation: ".$e);
+	SetupPage::log_error("An error happened while processing the installation: ".$e);
 }
 
 if (function_exists('memory_get_peak_usage'))
 {
 	if ($sOperation == 'file')
 	{
-		SetupWebPage::log_info("loading file '$sFileName', peak memory usage. ".memory_get_peak_usage());
+		SetupPage::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());
+		SetupPage::log_info("operation '$sOperation', peak memory usage. ".memory_get_peak_usage());
 	}
 }
 ?>

+ 3 - 3
setup/email.test.php

@@ -32,7 +32,7 @@ require_once(APPROOT.'/core/email.class.inc.php');
 require_once('./setuppage.class.inc.php');
 
 $sOperation = Utils::ReadParam('operation', 'step1');
-$oP = new SetupWebPage('iTop email test utility');
+$oP = new SetupPage('iTop email test utility');
 
 
 /**
@@ -110,7 +110,7 @@ function CheckEmailSetting($oP)
  * Display the form for the first step of the test wizard
  * which consists in a basic check of the configuration and display of a form for testing
  */  
-function DisplayStep1(SetupWebPage $oP)
+function DisplayStep1(SetupPage $oP)
 {
 	$sNextOperation = 'step2';
 	$oP->add("<h1>iTop email test</h1>\n");
@@ -145,7 +145,7 @@ function DisplayStep1(SetupWebPage $oP)
  * Display the form for the second step of the configuration wizard
  * which consists in sending an email, which maybe a problem under Windows
  */  
-function DisplayStep2(SetupWebPage $oP, $sFrom, $sTo)
+function DisplayStep2(SetupPage $oP, $sFrom, $sTo)
 {
 	//$sNextOperation = 'step3';
 	$oP->add("<h1>iTop configuration wizard</h1>\n");

+ 61 - 135
setup/index.php

@@ -40,7 +40,7 @@ define('MIN_MEMORY_LIMIT', 32*1024*1024);
 define('SUHOSIN_GET_MAX_VALUE_LENGTH', 2048); 
 
 $sOperation = Utils::ReadParam('operation', 'step0');
-$oP = new SetupWebPage('iTop configuration wizard');
+$oP = new SetupPage('iTop configuration wizard');
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 // Various helper function
@@ -120,7 +120,7 @@ function GetUploadTmpDir()
  * is compatible with the application
  * @return boolean true if this is Ok, false otherwise
  */
-function CheckPHPVersion(SetupWebPage $oP)
+function CheckPHPVersion(SetupPage $oP)
 {
 	$bResult = true;
 	$aErrors = array();
@@ -380,7 +380,7 @@ function CheckPHPVersion(SetupWebPage $oP)
  * the existing databases
  * @return Array The list of databases found in the server
  */
-function CheckServerConnection(SetupWebPage $oP, $sDBServer, $sDBUser, $sDBPwd)
+function CheckServerConnection(SetupPage $oP, $sDBServer, $sDBUser, $sDBPwd)
 {
 	$aResult = array();
 	$oP->log('Info - CheckServerConnection');
@@ -442,14 +442,14 @@ function CheckServerConnection(SetupWebPage $oP, $sDBServer, $sDBUser, $sDBPwd)
 /**
  * Scans the ./data directory for XML files and output them as a Javascript array
  */ 
-function PopulateDataFilesList(SetupWebPage $oP, $aParamValues, $oConfig)
+function PopulateDataFilesList(SetupPage $oP, $aParamValues, $oConfig)
 {
 
 	$sScript = "function PopulateDataFilesList()\n";
 	$sScript .= "{\n";
 	$sScript .= "if (aFilesToLoad.length > 0)  return;"; // Populate the list only once...
 
-	$aAvailableModules = AnalyzeInstallation($oConfig);
+	$aAvailableModules = AnalyzeInstallation($oConfig, $aParamValues['source_dir']);
 
 	$sMode = $aParamValues['mode'];
 	$aStructureDataFiles = array();
@@ -501,11 +501,11 @@ function PopulateDataFilesList(SetupWebPage $oP, $aParamValues, $oConfig)
 
 /**
  * Add some parameters as hidden inputs into a form
- * @param SetupWebpage $oP The page to insert the form elements into
+ * @param SetupPage $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())
+function AddParamsToForm(SetupPage $oP, $aParamValues, $aExcludeParams = array())
 {
 	foreach($aParamValues as $sName => $value)
 	{
@@ -537,64 +537,37 @@ function AddHiddenParam($oP, $sName, $value)
 }
 
 /**
- * Build the config file from the parameters (especially the selected modules)
- */
-function BuildConfig(SetupWebpage $oP, Config &$oConfig, $aParamValues, $aAvailableModules)
+ * Helper function to get the available languages from the given directory
+ * @param $sDir Path to the dictionary
+ * @return an array of language code => description
+ */    
+function GetAvailableLanguages($sDir)
 {
-	// Initialize the arrays below with default values for the application...
-	$oEmptyConfig = new Config('dummy_file', false); // Do NOT load any config file, just set the default values
-	$aAddOns = $oEmptyConfig->GetAddOns();
-	$aAppModules = $oEmptyConfig->GetAppModules();
-	$aDataModels = $oEmptyConfig->GetDataModels();
-	$aWebServiceCategories = $oEmptyConfig->GetWebServiceCategories();
-	$aDictionaries = $oEmptyConfig->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)
+	require_once(APPROOT.'/core/coreexception.class.inc.php');
+	require_once(APPROOT.'/core/dict.class.inc.php');
+
+	$aFiles = scandir($sDir);
+	foreach($aFiles as $sFile)
 	{
-		$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']))
+		if ($sFile == '.' || $sFile == '..' || $sFile == '.svn')
 		{
-			$aDictionaries = array_unique(array_merge($aDictionaries, $aAvailableModules[$sModuleId]['dictionary']));
+			// Skip
+			continue;
 		}
-		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']))
+
+		$sFilePath = $sDir.'/'.$sFile;
+		if (is_file($sFilePath) && preg_match('/^.+\.dict.*\.php$/i', $sFilePath, $aMatches))
 		{
-			$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));
+			require_once($sFilePath);
 		}
 	}
-	$oConfig->SetAddOns($aAddOns);
-	$oConfig->SetAppModules($aAppModules);
-	$oConfig->SetDataModels($aDataModels);
-	$oConfig->SetWebServiceCategories($aWebServiceCategories);
-	$oConfig->SetDictionaries($aDictionaries);
+
+	return Dict::GetLanguages();
 }
 
+
+
+
 /////////////////////////////////////////////////////////////////////////////////////////////////////////////
 // Handling of the different steps of the setup wizard
 /////////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -602,7 +575,7 @@ function BuildConfig(SetupWebpage $oP, Config &$oConfig, $aParamValues, $aAvaila
 /**
  * Displays the welcome screen and check some basic prerequisites
  */
-function WelcomeAndCheckPrerequisites(SetupWebPage $oP, $aParamValues, $iCurrentStep)
+function WelcomeAndCheckPrerequisites(SetupPage $oP, $aParamValues, $iCurrentStep)
 {
 	$sNextOperation = 'step'.($iCurrentStep+1);
 	$aParamValues['previous_step'] = 0;
@@ -620,9 +593,8 @@ function WelcomeAndCheckPrerequisites(SetupWebPage $oP, $aParamValues, $iCurrent
 	if (file_exists(FINAL_CONFIG_FILE))
 	{
 		$oConfig = new Config(FINAL_CONFIG_FILE);
-		$oConfig->WriteToFile(TMP_CONFIG_FILE);
 		
-		$aVersion = AnalyzeInstallation($oConfig);
+		$aVersion = AnalyzeInstallation($oConfig, $aParamValues['source_dir']);
 		if (!empty($aVersion[ROOT_MODULE]['version_db']))
 		{
 			$aPreviousParams = array('mode', 'db_server', 'db_user', 'db_pwd','db_name', 'new_db_name', 'db_prefix');
@@ -641,6 +613,8 @@ function WelcomeAndCheckPrerequisites(SetupWebPage $oP, $aParamValues, $iCurrent
 			AddHiddenParam($oP, 'db_name', $oConfig->GetDBName());
 			AddHiddenParam($oP, 'db_prefix', $oConfig->GetDBSubname());
 			AddHiddenParam($oP, 'mode', $sMode);
+			AddHiddenParam($oP, 'source_dir', 'whichsourcedir');
+			AddHiddenParam($oP, 'target_dir', 'whichtargetdir');
 			if (CheckPHPVersion($oP))
 			{
 				$oP->add("<h2 class=\"next\">Next: Licence agreement</h2>\n");
@@ -664,7 +638,12 @@ function WelcomeAndCheckPrerequisites(SetupWebPage $oP, $aParamValues, $iCurrent
 		$oP->p("<input id=\"choice_upgrade\" type=\"radio\" value=\"upgrade\" $sChecked name=\"mode\"><label for=\"choice_upgrade\">&nbsp;Upgrade an existing iTop instance</label>");
 		$oP->add("<h2 class=\"next\">Next: Licence agreement</h2>\n");
 		$oP->add("<input type=\"hidden\" name=\"operation\" value=\"$sNextOperation\">\n");
-		$aPreviousParams = array('mode');
+
+		//AddHiddenParam($oP, 'source_dir', 'datamodel');
+		AddHiddenParam($oP, 'source_dir', 'modules'); // not ready for the big bang ?
+		AddHiddenParam($oP, 'target_dir', 'env-production');
+		
+		$aPreviousParams = array('mode', 'source_dir', 'target_dir');
 		AddParamsToForm($oP, $aParamValues, $aPreviousParams);
 		$oP->add("<table style=\"width:100%\"><tr>\n");
 		$oP->add("<td style=\"text-align:right;\"><button type=\"submit\">Next >></button></td>\n");
@@ -709,7 +688,7 @@ function LicenceAcknowledgement($oP, $aParamValues, $iCurrentStep)
  * Display the form for the first step of the configuration wizard
  * which consists in the database server selection
  */  
-function DatabaseServerSelection(SetupWebPage $oP, $aParamValues, $iCurrentStep)
+function DatabaseServerSelection(SetupPage $oP, $aParamValues, $iCurrentStep)
 {
 	$sNextOperation = 'step'.($iCurrentStep+1);
 	$iPrevStep = 1;
@@ -752,7 +731,7 @@ function DatabaseServerSelection(SetupWebPage $oP, $aParamValues, $iCurrentStep)
  * 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)
+function DatabaseInstanceSelection(SetupPage $oP, $aParamValues, $iCurrentStep, $oConfig)
 {
 	$sNextOperation = 'step'.($iCurrentStep+1);
 	$iPrevStep = 2;
@@ -773,12 +752,6 @@ function DatabaseInstanceSelection(SetupWebPage $oP, $aParamValues, $iCurrentSte
 	}
 	else
 	{
-		// Connection is Ok, save it and continue the setup wizard
-		$oConfig->SetDBHost($sDBServer);
-		$oConfig->SetDBUser($sDBUser);
-		$oConfig->SetDBPwd($sDBPwd);
-		$oConfig->WriteToFile();
-
 		$oP->add("<fieldset><legend>Select the database instance to use for iTop<span class=\"hilite\">*</span></legend>\n");
 		$aForm = array();
 		$bExistingChecked = false;
@@ -841,28 +814,17 @@ function DatabaseInstanceSelection(SetupWebPage $oP, $aParamValues, $iCurrentSte
 /**
  * Display the form to select the iTop modules to be installed
  */  
-function ModulesSelection(SetupWebPage $oP, $aParamValues, $iCurrentStep, $oConfig)
+function ModulesSelection(SetupPage $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("<form id=\"theForm\" method=\"post\">\n");
 	AddParamsToForm($oP, $aParamValues, array('module'));
 	$sRedStar = '<span class="hilite">*</span>';
 	$oP->set_title("iTop modules selection");
 
-	$aAvailableModules = AnalyzeInstallation($oConfig);
+	$aAvailableModules = AnalyzeInstallation($oConfig, $aParamValues['source_dir']);
 	
 	// Form goes here
 	if ($aParamValues['mode'] == 'upgrade')
@@ -1040,7 +1002,7 @@ function ModulesSelection(SetupWebPage $oP, $aParamValues, $iCurrentStep, $oConf
  * 2) Creating the database structure  
  * 3) Prompting for the admin account to be created  
  */  
-function AdminAccountDefinition(SetupWebPage $oP, $aParamValues, $iCurrentStep, Config $oConfig)
+function AdminAccountDefinition(SetupPage $oP, $aParamValues, $iCurrentStep, Config $oConfig)
 {
 	$sNextOperation = 'step'.($iCurrentStep+1);
 	$aParamValues['previous_step'] = $iCurrentStep; // Come back here	
@@ -1050,15 +1012,11 @@ function AdminAccountDefinition(SetupWebPage $oP, $aParamValues, $iCurrentStep,
 	$oP->add("<input type=\"hidden\" name=\"operation\" value=\"$sNextOperation\">\n");
 	AddParamsToForm($oP, $aParamValues, array('auth_user', 'auth_pwd', 'language'));
 
-	$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
 	$sRedStar = "<span class=\"hilite\">*</span>";
 	$oP->add("<h2>Default language for the application:</h2>\n");
 	// Possible languages (depends on the dictionaries loaded in the config)
 	$aForm = array();
-	$aAvailableLanguages = Dict::GetLanguages();
+	$aAvailableLanguages = GetAvailableLanguages(APPROOT.'dictionaries');
 	$sLanguages = '';
 	$sDefaultCode = $oConfig->GetDefaultLanguage();
 	foreach($aAvailableLanguages as $sLangCode => $aInfo)
@@ -1093,7 +1051,7 @@ function AdminAccountDefinition(SetupWebPage $oP, $aParamValues, $iCurrentStep,
  * Display the form for validating/entering the URL (path) to the application
  * which consists in
  */  
-function ApplicationPathSelection(SetupWebPage $oP, $aParamValues, $iCurrentStep, Config $oConfig)
+function ApplicationPathSelection(SetupPage $oP, $aParamValues, $iCurrentStep, Config $oConfig)
 {
 	$sNextOperation = 'step7';
 	if ($aParamValues['mode'] == 'upgrade')
@@ -1139,43 +1097,17 @@ function ApplicationPathSelection(SetupWebPage $oP, $aParamValues, $iCurrentStep
  * 1) Creating the admin user account
  * 2) Prompting to load some sample data  
  */  
-function SampleDataSelection(SetupWebPage $oP, $aParamValues, $iCurrentStep, Config $oConfig)
+function SampleDataSelection(SetupPage $oP, $aParamValues, $iCurrentStep, Config $oConfig)
 {
 	$sNextOperation = 'step8';
 	$iPrevStep = 6;
 
 	$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
-
-	$oConfig->Set('app_root_url', $aParamValues['application_path']);
-	// in case of upgrade, the value is already present in the config file
-	$oConfig->WriteToFile(TMP_CONFIG_FILE);
 
 	$oP->add("<form id=\"theForm\" method=\"post\"\">\n");
 	$oP->add("<input type=\"hidden\" name=\"operation\" value=\"$sNextOperation\">\n");
 	AddParamsToForm($oP, $aParamValues, array('sample_data'));
 
-	InitDataModel(TMP_CONFIG_FILE, true);  // load data model and connect to the database
-	$aAvailableModules = GetAvailableModules($oP);
-	foreach($aParamValues['module'] as $sModuleId)
-	{
-		if (isset($aAvailableModules[$sModuleId]['installer']))
-		{
-			$sModuleInstallerClass = $aAvailableModules[$sModuleId]['installer'];
-			// The validity of the sModuleInstallerClass has been established in BuildConfig() 
-			$aCallSpec = array($sModuleInstallerClass, 'AfterDatabaseCreation');
-			call_user_func_array($aCallSpec, array($oConfig));
-		}
-	}
-
 	$oP->add("<h2>Loading of sample data</h2>\n");
 	$oP->p("<fieldset><legend> Do you want to load sample data into the database ? </legend>\n");
 	$sChecked = ($aParamValues['sample_data'] == 'no') ? '' : 'checked';
@@ -1194,13 +1126,10 @@ function SampleDataSelection(SetupWebPage $oP, $aParamValues, $iCurrentStep, Con
 /**
  * Displays the summary of the actions to be taken
  */
-function DisplaySummary(SetupWebPage $oP, $aParamValues, $iCurrentStep, Config $oConfig)
+function DisplaySummary(SetupPage $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
+	$aAvailableModules = AnalyzeInstallation($oConfig, $aParamValues['source_dir']);
 	
 	$aInstall = array();
 	$aUpgrade = array();
@@ -1260,7 +1189,7 @@ function DisplaySummary(SetupWebPage $oP, $aParamValues, $iCurrentStep, Config $
 		// Admin account
 		$oP->collapsible('admin_account', "Administrator account", array('Login:'.htmlentities($aParamValues['auth_user'], ENT_QUOTES, 'UTF-8')));
 		// Default language
-		$aAvailableLanguages = Dict::GetLanguages();
+		$aAvailableLanguages = GetAvailableLanguages(APPROOT.'dictionaries');
 		$oP->collapsible('language', "Default application language", array( $aAvailableLanguages[$aParamValues['language']]['description']." (".$aAvailableLanguages[$aParamValues['language']]['localized_description'].")"));
 		$oP->add('</div>');
 		
@@ -1415,7 +1344,7 @@ EOF
  * 1) Creating the final configuration file
  * 2) Prompting the user to make the file read-only  
  */  
-function SetupFinished(SetupWebPage $oP, $aParamValues, $iCurrentStep, Config $oConfig)
+function SetupFinished(SetupPage $oP, $aParamValues, $iCurrentStep, Config $oConfig)
 {
 	$sAuthUser = $aParamValues['auth_user'];
 	$sAuthPwd = $aParamValues['auth_pwd'];
@@ -1437,12 +1366,14 @@ function SetupFinished(SetupWebPage $oP, $aParamValues, $iCurrentStep, Config $o
 		// NON case sensitive
 		$oConfig->SetDBCollation('utf8_unicode_ci');
 		
-		
+		// Final config update: add the modules
+		UpdateConfigSettings($oConfig, $aParamValues, $aParamValues['target_dir']);
+
 		// 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
+		InitDataModel($oConfig, false, true); // Load model, startup DB and load the cache
 		if ($aParamValues['mode'] == 'install')
 		{
 			if (UserRights::CheckCredentials($sAuthUser, $sAuthPwd))
@@ -1477,7 +1408,7 @@ function SetupFinished(SetupWebPage $oP, $aParamValues, $iCurrentStep, Config $o
 		$oP->add("<form id=\"theForm\" method=\"get\" action=\"../index.php\">\n");
 
 		// Check if there are some manual steps required:
-		$aAvailableModules = AnalyzeInstallation($oConfig);
+		$aAvailableModules = AnalyzeInstallation($oConfig, $aParamValues['target_dir']);
 		$aManualSteps = array();
 		$sRootUrl = utils::GetAbsoluteUrlAppRoot();
 		foreach($aParamValues['module'] as $sModuleId)
@@ -1535,7 +1466,7 @@ ini_set('max_execution_time', max(240, ini_get('max_execution_time')));
 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', 'application_path');
+$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', 'application_path', 'source_dir', 'target_dir');
 foreach($aParams as $sName)
 {
 	$aParamValues[$sName] = utils::ReadParam($sName, '', false, 'raw_data');
@@ -1578,15 +1509,10 @@ else
 	}
 	
 }
-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 */);
-}
+
+$oConfig = new Config(TMP_CONFIG_FILE, false /* Don't try to load it */);
+UpdateConfigSettings($oConfig, $aParamValues);
+
 try
 {
 	switch($sOperation)

+ 60 - 6
setup/setup.js

@@ -136,8 +136,8 @@ function DoSubmit(sMsg, iStep)
 		case 7: // Sample data selection
 			break;
 			
-		case 8: // Display Summary: launch DoUpdateDBSchema to start the asynchronous update
-		bResult = DoUpdateDBSchema();
+		case 8: // Display Summary: launch DoCompileDataModel to start the asynchronous update
+		bResult = DoCompileDataModel();
 		break;
 
 		// Email test page
@@ -159,19 +159,51 @@ function DoSubmit(sMsg, iStep)
 	return bResult;
 }
 
+function DoCompileDataModel()
+{
+	try
+	{
+		// Call the asynchronous page that performs the compilation of the data model and the creation of the configuration file
+		$('#log').html('');
+		$('#setup').block({message: '<p><span id="setup_msg">Preparing data model...</span><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': 'compile_data_model',
+							'selected_modules': GetSelectedModules(),
+							'mode': $(':input[name=mode]').val(),
+							'source_dir': $(':input[name=source_dir]').val(),
+							'target_dir': $(':input[name=target_dir]').val()
+						},
+						DoUpdateDBSchema, 'html');
+	}
+	catch(err)
+	{
+		alert('An exception occured: '+err);
+	}
+	return false; // Do NOT submit the form yet
+}
+
 function DoUpdateDBSchema()
 {
 	try
 	{
 		// Call the asynchronous page that performs the creation/update of the DB Schema
 		$('#log').html('');
-		$('#setup').block({message: '<p><span id="setup_msg">Updating DB schema...</span><br/><div id=\"progress\">0%</div></p>'});
-		$('#progress').progression( {Current:5, Maximum: 100, aBackgroundImg: 'orange-progress.gif', aTextColor: '#000000'} );
+		$('#setup').block({message: '<p><span id="setup_msg">Updating DB schema...</span><br/><div id=\"progress\">5%</div></p>'});
+		$('#progress').progression( {Current:10, 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()
+							'mode': $(':input[name=mode]').val(),
+							'db_server': $(':input[name=db_server]').val(),
+							'db_user': $(':input[name=db_user]').val(),
+							'db_pwd': $(':input[name=db_pwd]').val(),
+							'db_name': $(':input[name=db_name]').val(),
+							'new_db_name': $(':input[name=new_db_name]').val(),
+							'db_prefix': $(':input[name=db_prefix]').val(),
+							'modules_dir': $(':input[name=target_dir]').val()
 						},
 						DoUpdateProfiles, 'html');
 	}
@@ -200,6 +232,13 @@ function DoUpdateProfiles(response, status, xhr)
 					'operation': 'after_db_create',
 					'selected_modules': GetSelectedModules(),
 					'mode': $(':input[name=mode]').val(),
+					'db_server': $(':input[name=db_server]').val(),
+					'db_user': $(':input[name=db_user]').val(),
+					'db_pwd': $(':input[name=db_pwd]').val(),
+					'db_name': $(':input[name=db_name]').val(),
+					'new_db_name': $(':input[name=new_db_name]').val(),
+					'db_prefix': $(':input[name=db_prefix]').val(),
+					'modules_dir': $(':input[name=target_dir]').val(),
 					'auth_user': $(':input[name=auth_user]').val(),
 					'auth_pwd': $(':input[name=auth_pwd]').val(),
 					'language': $(':input[name=language]').val()
@@ -294,7 +333,22 @@ function LoadNextDataFile(response, status, xhr)
 			//alert('Loading file '+sFileName+' ('+iPercent+' %) - '+sSessionStatus);
 			$("#progress").progression({ Current: iPercent, Maximum: 100, aBackgroundImg: 'orange-progress.gif', aTextColor: '#000000' });
 			iCounter++;
-			$('#log').load( 'ajax.dataloader.php', { 'operation': 'load_data', 'file': sFileName, 'percent': iPercent, 'session_status': sSessionStatus }, LoadNextDataFile, 'html');
+			$('#log').load( 'ajax.dataloader.php',
+				{
+					'selected_modules': GetSelectedModules(),
+					'db_server': $(':input[name=db_server]').val(),
+					'db_user': $(':input[name=db_user]').val(),
+					'db_pwd': $(':input[name=db_pwd]').val(),
+					'db_name': $(':input[name=db_name]').val(),
+					'new_db_name': $(':input[name=new_db_name]').val(),
+					'db_prefix': $(':input[name=db_prefix]').val(),
+					'modules_dir': $(':input[name=target_dir]').val(),
+					'operation': 'load_data',
+					'file': sFileName,
+					'percent': iPercent,
+					'session_status': sSessionStatus
+				},
+				LoadNextDataFile, 'html');
 		}
 		else
 		{

+ 108 - 201
setup/setuppage.class.inc.php

@@ -24,6 +24,8 @@
  */
 
 require_once(APPROOT."/application/nicewebpage.class.inc.php");
+require_once(APPROOT."designer/modulediscovery.class.inc.php");
+
 define('INSTALL_LOG_FILE', APPROOT.'/setup.log');
 
 define ('MODULE_ACTION_OPTIONAL', 1);
@@ -32,7 +34,7 @@ define ('MODULE_ACTION_IMPOSSIBLE', 3);
 define ('ROOT_MODULE', '_Root_'); // Convention to store IN MEMORY the name/version of the root module i.e. application
 
 date_default_timezone_set('Europe/Paris');
-class SetupWebPage extends NiceWebPage
+class SetupPage extends NiceWebPage
 {
     public function __construct($sTitle)
     {
@@ -279,146 +281,120 @@ h3.clickable.open {
 			fclose($hLogFile);
 		}
 	}
-	
+} // End of class
 
-	static $m_aModuleArgs = array(
-		'label' => 'One line description shown during the interactive setup',
-		'dependencies' => 'array of module ids',
-		'mandatory' => 'boolean',
-		'visible' => 'boolean',
-		'datamodel' =>  'array of data model files',
-		//'dictionary' => 'array of dictionary files', // No longer mandatory, now automated
-		'data.struct' => 'array of structural data files',
-		'data.sample' => 'array of sample data files',
-		'doc.manual_setup' => 'url',
-		'doc.more_information' => 'url',
-	);
-	
-	static $m_aModules = array();
-	
-	// All the entries below are list of file paths relative to the module directory
-	static $m_aFilesList = array('datamodel', 'webservice', 'dictionary', 'data.struct', 'data.sample');
 
-	static $m_sModulePath = null;
-	public static function SetModulePath($sModulePath)
+/**
+ * Helper function to initialize a configuration from the page arguments
+ */
+function UpdateConfigSettings(&$oConfig, $aParamValues, $sModulesDir = null)
+{
+	if (isset($aParamValues['application_path']))
 	{
-		self::$m_sModulePath = $sModulePath;
+		$oConfig->Set('app_root_url', $aParamValues['application_path']);
 	}
-
-	public static function AddModule($sFilePath, $sId, $aArgs)
+	if (isset($aParamValues['mode']) && isset($aParamValues['language']))
 	{
-		if (!array_key_exists('itop_version', $aArgs))
+		if (($aParamValues['mode'] == 'install') ||  $oConfig->GetDefaultLanguage() == '')
 		{
-			// Assume 1.0.2
-			$aArgs['itop_version'] = '1.0.2';
+			$oConfig->SetDefaultLanguage($aParamValues['language']);
 		}
-		foreach (self::$m_aModuleArgs as $sArgName => $sArgDesc)
+	}
+	if (isset($aParamValues['db_server']))
+	{
+		$oConfig->SetDBHost($aParamValues['db_server']);
+		$oConfig->SetDBUser($aParamValues['db_user']);
+		$oConfig->SetDBPwd($aParamValues['db_pwd']);
+		$sDBName = $aParamValues['db_name'];
+		if ($sDBName == '')
 		{
-			if (!array_key_exists($sArgName, $aArgs))
-			{
-				throw new Exception("Module '$sId': missing argument '$sArgName'");
-		   }
+			$sDBName = $aParamValues['new_db_name'];
 		}
+		$oConfig->SetDBName($sDBName);
+		$oConfig->SetDBSubname($aParamValues['db_prefix']);
+	}
 
-		self::$m_aModules[$sId] = $aArgs;
+	if (!is_null($sModulesDir))
+	{
+		if (isset($aParamValues['selected_modules']))
+		{
+			$aSelectedModules = explode(',', $aParamValues['selected_modules']);
+		}
+		else
+		{
+			$aSelectedModules = null;
+		}
 
-		foreach(self::$m_aFilesList as $sAttribute)
+		// Initialize the arrays below with default values for the application...
+		$oEmptyConfig = new Config('dummy_file', false); // Do NOT load any config file, just set the default values
+		$aAddOns = $oEmptyConfig->GetAddOns();
+		$aAppModules = $oEmptyConfig->GetAppModules();
+		$aDataModels = $oEmptyConfig->GetDataModels();
+		$aWebServiceCategories = $oEmptyConfig->GetWebServiceCategories();
+		$aDictionaries = $oEmptyConfig->GetDictionaries();
+		// Merge the values with the ones provided by the modules
+		// Make sure when don't load the same file twice...
+
+		$aModules = ModuleDiscovery::GetAvailableModules(APPROOT, $sModulesDir);
+		foreach($aModules as $sModuleId => $aModuleInfo)
 		{
-			if (isset(self::$m_aModules[$sId][$sAttribute]))
+			list($sModuleName, $sModuleVersion) = ModuleDiscovery::GetModuleName($sModuleId);
+			if (is_null($aSelectedModules) || in_array($sModuleName, $aSelectedModules))
 			{
-				// All the items below are list of files, that are relative to the current file
-				// being loaded, let's update their path to store path relative to the application directory
-				foreach(self::$m_aModules[$sId][$sAttribute] as $idx => $sRelativePath)
+				if (isset($aModuleInfo['datamodel']))
 				{
-					self::$m_aModules[$sId][$sAttribute][$idx] = self::$m_sModulePath.'/'.$sRelativePath;
+					$aDataModels = array_unique(array_merge($aDataModels, $aModuleInfo['datamodel']));
 				}
-			}
-		}
-		// Populate automatically the list of dictionary files
-		if(preg_match('|^([^/]+)|', $sId, $aMatches)) // ModuleName = everything before the first forward slash
-		{
-			$sModuleName = $aMatches[1];
-			$sDir = dirname($sFilePath);
-			if ($hDir = opendir($sDir))
-			{
-				while (($sFile = readdir($hDir)) !== false)
+				if (isset($aModuleInfo['webservice']))
 				{
-					$aMatches = array();
-					if (preg_match("/^[^\\.]+.dict.$sModuleName.php$/i", $sFile, $aMatches)) // Dictionary files named like <Lang>.dict.<ModuleName>.php are loaded automatically
-					{
-						self::$m_aModules[$sId]['dictionary'][] = self::$m_sModulePath.'/'.$sFile;
-					}
+					$aWebServiceCategories = array_unique(array_merge($aWebServiceCategories, $aModuleInfo['webservice']));
 				}
-				closedir($hDir);
-			}
-		}
-	}
-	public static function GetModules($oP = null)
-	{
-		// Order the modules to take into account their inter-dependencies
-		$aDependencies = array();
-		foreach(self::$m_aModules as $sId => $aModule)
-		{
-			$aDependencies[$sId] = $aModule['dependencies'];
-		}
-		$aOrderedModules = array();
-		$iLoopCount = 1;
-		while(($iLoopCount < count(self::$m_aModules)) && (count($aDependencies) > 0) )
-		{
-			foreach($aDependencies as $sId => $aRemainingDeps)
-			{
-				$bDependenciesSolved = true;
-				foreach($aRemainingDeps as $sDepId)
+				if (isset($aModuleInfo['dictionary']))
+				{
+					$aDictionaries = array_unique(array_merge($aDictionaries, $aModuleInfo['dictionary']));
+				}
+				if (isset($aModuleInfo['settings']))
 				{
-					if (!in_array($sDepId, $aOrderedModules))
+					foreach($aModuleInfo['settings'] as $sProperty => $value)
 					{
-						$bDependenciesSolved = false;
+						list($sName, $sVersion) = ModuleDiscovery::GetModuleName($sModuleId);
+						$oConfig->SetModuleSetting($sName, $sProperty, $value);
 					}
 				}
-				if ($bDependenciesSolved)
+				if (isset($aModuleInfo['installer']))
 				{
-					$aOrderedModules[] = $sId;
-					unset($aDependencies[$sId]);
+					$sModuleInstallerClass = $aModuleInfo['installer'];
+					if (!class_exists($sModuleInstallerClass))
+					{
+						throw new Exception("Wrong installer class: '$sModuleInstallerClass' is not a PHP class - Module: ".$aModuleInfo['label']);
+					}
+					if (!is_subclass_of($sModuleInstallerClass, 'ModuleInstallerAPI'))
+					{
+						throw new Exception("Wrong installer class: '$sModuleInstallerClass' is not derived from 'ModuleInstallerAPI' - Module: ".$aModuleInfo['label']);
+					}
+					$aCallSpec = array($sModuleInstallerClass, 'BeforeWritingConfig');
+					$oConfig = call_user_func_array($aCallSpec, array($oConfig));
 				}
 			}
-			$iLoopCount++;
-		}
-		if (count($aDependencies) >0)
-		{
-			$sHtml = "<ul><b>Warning: the following modules have unmet dependencies, and have been ignored:</b>\n";			
-			foreach($aDependencies as $sId => $aDeps)
-			{
-				$aModule = self::$m_aModules[$sId];
-				$sHtml.= "<li>{$aModule['label']} (id: $sId), depends on: ".implode(', ', $aDeps)."</li>";
-			}
-			$sHtml .= "</ul>\n";
-			if (is_object($oP))
-			{
-				$oP->warning($sHtml);
-			}
-			else
-			{
-				self::log_warning($sHtml);
-			}
-		}
-		// Return the ordered list, so that the dependencies are met...
-		$aResult = array();
-		foreach($aOrderedModules as $sId)
-		{
-			$aResult[$sId] = self::$m_aModules[$sId];
 		}
-		return $aResult;
+		$oConfig->SetAddOns($aAddOns);
+		$oConfig->SetAppModules($aAppModules);
+		$oConfig->SetDataModels($aDataModels);
+		$oConfig->SetWebServiceCategories($aWebServiceCategories);
+		$oConfig->SetDictionaries($aDictionaries);
 	}
-} // 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 $oConfig object The configuration (volatile, not necessarily already on disk)
  * @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)
+function InitDataModel($oConfig, $bModelOnly = true, $bUseCache = false)
 {
 	require_once(APPROOT.'/core/log.class.inc.php');
 	require_once(APPROOT.'/core/kpi.class.inc.php');
@@ -437,40 +413,30 @@ function InitDataModel($sConfigFileName, $bModelOnly = true, $bUseCache = false)
 	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)");
+
+	$sConfigFile = $oConfig->GetLoadedFile();
+	if (strlen($sConfigFile) > 0)
+	{
+		SetupPage::log_info("MetaModel::Startup from $sConfigFile (ModelOnly = $bModelOnly)");
+	}
+	else
+	{
+		SetupPage::log_info("MetaModel::Startup (ModelOnly = $bModelOnly)");
+	}
 
 	if ($bUseCache)
 	{
 		// Reset the cache for the first use !
-		$oConfig = new Config($sConfigFileName, true /* Load Config */);
 		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);
+	MetaModel::Startup($oConfig, $bModelOnly, $bUseCache);
 }
 
 /**
  * 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
+ * @param $oConfig Config Defines the target environment (DB)
  * @return hash Array with the following format:
  * array =>
  *     'iTop' => array(
@@ -494,7 +460,7 @@ function GetAvailableModules($oP = null)
  *     )
  * )
  */     
-function AnalyzeInstallation($oConfig)
+function AnalyzeInstallation($oConfig, $sModulesRelativePath)
 {
 	$aRes = array(
 		ROOT_MODULE => array(
@@ -505,10 +471,10 @@ function AnalyzeInstallation($oConfig)
 		)
 	);
 
-	$aModules = GetAvailableModules();
+	$aModules = ModuleDiscovery::GetAvailableModules(APPROOT, $sModulesRelativePath);
 	foreach($aModules as $sModuleId => $aModuleInfo)
 	{
-		list($sModuleName, $sModuleVersion) = GetModuleName($sModuleId);
+		list($sModuleName, $sModuleVersion) = ModuleDiscovery::GetModuleName($sModuleId);
 
 		$sModuleAppVersion = $aModuleInfo['itop_version'];
 		$aModuleInfo['version_db'] = '';
@@ -615,39 +581,19 @@ function AnalyzeInstallation($oConfig)
 	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)
+function CreateDatabaseStructure(Config $oConfig, $sMode)
 {
 	if (strlen($oConfig->GetDBSubname()) > 0)
 	{
-		SetupWebPage::log_info("Creating the structure in '".$oConfig->GetDBName()."' (table names prefixed by '".$oConfig->GetDBSubname()."').");
+		SetupPage::log_info("Creating the structure in '".$oConfig->GetDBName()."' (table names prefixed by '".$oConfig->GetDBSubname()."').");
 	}
 	else
 	{
-		SetupWebPage::log_info("Creating the structure in '".$oConfig->GetDBSubname()."'.");
+		SetupPage::log_info("Creating the structure in '".$oConfig->GetDBSubname()."'.");
 	}
 
 	//MetaModel::CheckDefinitions();
@@ -656,7 +602,7 @@ function CreateDatabaseStructure(Config $oConfig, $aSelectedModules, $sMode)
 		if (!MetaModel::DBExists(/* bMustBeComplete */ false))
 		{
 			MetaModel::DBCreate();
-			SetupWebPage::log_ok("Database structure successfully created.");
+			SetupPage::log_ok("Database structure successfully created.");
 		}
 		else
 		{
@@ -675,19 +621,19 @@ function CreateDatabaseStructure(Config $oConfig, $aSelectedModules, $sMode)
 		if (MetaModel::DBExists(/* bMustBeComplete */ false))
 		{
 			MetaModel::DBCreate();
-			SetupWebPage::log_ok("Database structure successfully updated.");
+			SetupPage::log_ok("Database structure successfully updated.");
 
 			// Check (and update only if it seems needed) the hierarchical keys
 			ob_start();
 			MetaModel::CheckHKeys(false /* bDiagnosticsOnly */, true /* bVerbose*/, true /* bForceUpdate */); // Since in 1.2-beta the detection was buggy, let's force the rebuilding of HKeys
 			$sFeedback = ob_get_clean();
-			SetupWebPage::log_ok("Hierchical keys rebuilt: $sFeedback");
+			SetupPage::log_ok("Hierchical keys rebuilt: $sFeedback");
 
 			// Check (and fix) data sync configuration
 			ob_start();
 			MetaModel::CheckDataSources(false /*$bDiagnostics*/, true/*$bVerbose*/);
 			$sFeedback = ob_get_clean();
-			SetupWebPage::log_ok("Data sources checked: $sFeedback");
+			SetupPage::log_ok("Data sources checked: $sFeedback");
 		}
 		else
 		{
@@ -704,7 +650,7 @@ function CreateDatabaseStructure(Config $oConfig, $aSelectedModules, $sMode)
 	return true;
 }
 
-function RecordInstallation(Config $oConfig, $aSelectedModules)
+function RecordInstallation(Config $oConfig, $aSelectedModules, $sModulesRelativePath)
 {
 	// Record main installation
 	$oInstallRec = new ModuleInstallation();
@@ -716,7 +662,7 @@ function RecordInstallation(Config $oConfig, $aSelectedModules)
 
 	// Record installed modules
 	//
-	$aAvailableModules = AnalyzeInstallation($oConfig);
+	$aAvailableModules = AnalyzeInstallation($oConfig, $sModulesRelativePath);
 	foreach($aSelectedModules as $sModuleId)
 	{
 		$aModuleData = $aAvailableModules[$sModuleId];
@@ -757,43 +703,4 @@ function RecordInstallation(Config $oConfig, $aSelectedModules)
 	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.");
-	}
-}
 ?>

+ 6 - 6
setup/xmldataloader.class.inc.php

@@ -186,7 +186,7 @@ class XMLDataLoader
 		{
 			if (!MetaModel::IsValidClass($sClass))
 			{
-				SetupWebPage::log_error("Unknown class - $sClass");
+				SetupPage::log_error("Unknown class - $sClass");
 				throw(new Exception("Unknown class - $sClass"));
 			}
 
@@ -207,7 +207,7 @@ class XMLDataLoader
 				if (!MetaModel::IsValidAttCode($sClass, $sAttCode))
 				{
 					$sMsg = "Unknown attribute code - $sClass/$sAttCode";
-					SetupWebPage::log_error($sMsg);
+					SetupPage::log_error($sMsg);
 					throw(new Exception($sMsg));
 				}
 
@@ -229,7 +229,7 @@ class XMLDataLoader
 							else
 							{
 								$sMsg = "Ext key not reconcilied - $sClass/$iSrcId - $sAttCode: '".$sQuery."' - found $iMatches matche(s)";
-								SetupWebPage::log_error($sMsg);
+								SetupPage::log_error($sMsg);
 								$this->m_aErrors[] = $sMsg;
 								$iExtKey = 0;
 							}
@@ -271,7 +271,7 @@ class XMLDataLoader
 						{
 							// $res contains the error description
 							$sMsg = "Value not allowed - $sClass/$iSrcId - $sAttCode: '".$oSubNode."' ; $res";
-							SetupWebPage::log_error($sMsg);
+							SetupPage::log_error($sMsg);
 							$this->m_aErrors[] = $sMsg;
 						}
 						$oTargetObj->Set($sAttCode, $value);
@@ -352,7 +352,7 @@ class XMLDataLoader
 		}
 		catch(Exception $e)
 		{
-			SetupWebPage::log_error("An object could not be recorded - $sClass/$iSrcId - ".$e->getMessage());
+			SetupPage::log_error("An object could not be recorded - $sClass/$iSrcId - ".$e->getMessage());
 			$this->m_aErrors[] = "An object could not be recorded - $sClass/$iSrcId - ".$e->getMessage();
 		}
 		$aParentClasses = MetaModel::EnumParentClasses($sClass);
@@ -387,7 +387,7 @@ class XMLDataLoader
 						if ($iExtKey == 0)
 						{
 							$sMsg = "unresolved extkey in $sClass::".$oTargetObj->GetKey()."(".$oTargetObj->GetName().")::$sAttCode=$sTargetClass::$iTempKey";
-							SetupWebPage::log_warning($sMsg);
+							SetupPage::log_warning($sMsg);
 							$this->m_aWarnings[] = $sMsg;
 							//echo "<pre>aKeys[".$sTargetClass."]:\n";
 							//print_r($this->m_aKeys[$sTargetClass]);

+ 2 - 2
test/benchmark.php

@@ -709,7 +709,7 @@ class BenchmarkDataCreation
 /**
  * Ask the user what are the settings for the data load
  */  
-function DisplayStep1(SetupWebPage $oP)
+function DisplayStep1(SetupPage $oP)
 {
 	$sNextOperation = 'step2';
 	$oP->add("<h1>iTop benchmarking</h1>\n");
@@ -775,7 +775,7 @@ function DisplayStep1(SetupWebPage $oP)
 LoginWebPage::DoLogin(); // Check user rights and prompt if needed
 
 $sOperation = Utils::ReadParam('operation', 'step1');
-$oP = new SetupWebPage('iTop benchmark utility');
+$oP = new SetupPage('iTop benchmark utility');
 
 ExecutionKPI::EnableDuration();
 $oKPI = new ExecutionKPI();