Some progress on the 2.0 setup...

SVN:trunk[2237]
This commit is contained in:
Denis Flaven
2012-10-10 09:18:32 +00:00
parent 7dbbb1c299
commit 847a538912
13 changed files with 1101 additions and 358 deletions

View File

@@ -532,7 +532,7 @@ class Config
'type' => 'string',
'description' => 'Source directory for the datamodel files. (which gets compiled to env-production).',
// examples... not used
'default' => 'datamodels/latest',
'default' => '',
'value' => '',
'source_of_value' => '',
'show_in_conf_sample' => true,
@@ -1420,7 +1420,7 @@ class Config
/**
* Helper function to initialize a configuration from the page arguments
*/
public function UpdateFromParams($aParamValues, $sModulesDir = null)
public function UpdateFromParams($aParamValues, $sModulesDir = null, $bPreserveModuleSettings = false)
{
if (isset($aParamValues['application_path']))
{
@@ -1469,7 +1469,7 @@ class Config
// 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);
$aModules = ModuleDiscovery::GetAvailableModules(array(APPROOT.$sModulesDir));
foreach($aModules as $sModuleId => $aModuleInfo)
{
list($sModuleName, $sModuleVersion) = ModuleDiscovery::GetModuleName($sModuleId);
@@ -1489,10 +1489,17 @@ class Config
}
if (isset($aModuleInfo['settings']))
{
list($sName, $sVersion) = ModuleDiscovery::GetModuleName($sModuleId);
foreach($aModuleInfo['settings'] as $sProperty => $value)
{
list($sName, $sVersion) = ModuleDiscovery::GetModuleName($sModuleId);
$this->SetModuleSetting($sName, $sProperty, $value);
if ($bPreserveModuleSettings && isset($this->m_aModuleSettings[$sName][$sProperty]))
{
// Do nothing keep the original value
}
else
{
$this->SetModuleSetting($sName, $sProperty, $value);
}
}
}
if (isset($aModuleInfo['installer']))

BIN
images/extension.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

View File

@@ -136,18 +136,36 @@ try
switch($sOperation)
{
case 'async_action':
require_once(APPROOT.'/setup/wizardcontroller.class.inc.php');
require_once(APPROOT.'/setup/wizardsteps.class.inc.php');
ini_set('max_execution_time', max(240, ini_get('max_execution_time')));
// While running the setup it is desirable to see any error that may happen
ini_set('display_errors', true);
ini_set('display_startup_errors', true);
$sClass = utils::ReadParam('step_class', '');
$sState = utils::ReadParam('step_state', '');
$sActionCode = utils::ReadParam('code', '');
$aParams = utils::ReadParam('params', array(), false, 'raw_data');
$oPage = new ajax_page('');
$oDummyController = new WizardController('');
$oStep = new $sClass($oDummyController, $sState);
$oStep->AsyncAction($oPage, $sActionCode, $aParams);
$oPage->output();
$sConfigFile = utils::GetConfigFilePath();
if (file_exists($sConfigFile) && !is_writable($sConfigFile))
{
$oPage->error("<b>Error:</b> the configuration file '".$sConfigFile."' already exists and cannot be overwritten.");
$oPage->p("The wizard cannot modify the configuration file for you. If you want to upgrade ".ITOP_APPLICATION.", make sure that the file '<b>".realpath($sConfigFile)."</b>' can be modified by the web server.");
$oPage->output();
}
else
{
require_once(APPROOT.'/setup/wizardcontroller.class.inc.php');
require_once(APPROOT.'/setup/wizardsteps.class.inc.php');
$sClass = utils::ReadParam('step_class', '');
$sState = utils::ReadParam('step_state', '');
$sActionCode = utils::ReadParam('code', '');
$aParams = utils::ReadParam('params', array(), false, 'raw_data');
$oPage = new ajax_page('');
$oDummyController = new WizardController('');
if (is_subclass_of($sClass, 'WizardStep'))
{
$oStep = new $sClass($oDummyController, $sState);
$oStep->AsyncAction($oPage, $sActionCode, $aParams);
}
$oPage->output();
}
break;
//////////////////////////////

View File

@@ -126,7 +126,7 @@ class ApplicationInstaller
$aPreinstall = $this->oParams->Get('preinstall');
$aCopies = $aPreinstall['copies'];
// disabled - $sReport = self::DoCopy($aCopies);
$sReport = self::DoCopy($aCopies);
$sReport = "copy disabled...";
$aResult = array(
@@ -167,7 +167,8 @@ class ApplicationInstaller
case 'compile':
$aSelectedModules = $this->oParams->Get('selected_modules');
$sSourceDir = $this->oParams->Get('source_dir', 'datamodel');
$sSourceDir = $this->oParams->Get('source_dir', 'datamodels/latest');
$sExtensionDir = $this->oParams->Get('extensions_dir', 'extensions');
$sTargetEnvironment = $this->oParams->Get('target_env', '');
if ($sTargetEnvironment == '')
{
@@ -190,7 +191,7 @@ class ApplicationInstaller
}
}
self::DoCompile($aSelectedModules, $sSourceDir, $sTargetDir, $sWorkspaceDir, $bUseSymbolicLinks);
self::DoCompile($aSelectedModules, $sSourceDir, $sExtensionDir, $sTargetDir, $sWorkspaceDir, $bUseSymbolicLinks);
$aResult = array(
'status' => self::OK,
@@ -215,8 +216,9 @@ class ApplicationInstaller
$sDBPwd = $aDBParams['pwd'];
$sDBName = $aDBParams['name'];
$sDBPrefix = $aDBParams['prefix'];
$bOldAddon = $this->oParams->Get('old_addon', false);
self::DoUpdateDBSchema($sMode, $sTargetDir, $sDBServer, $sDBUser, $sDBPwd, $sDBName, $sDBPrefix, $sTargetEnvironment);
self::DoUpdateDBSchema($sMode, $sTargetDir, $sDBServer, $sDBUser, $sDBPwd, $sDBName, $sDBPrefix, $sTargetEnvironment, $bOldAddon);
$aResult = array(
'status' => self::OK,
@@ -247,8 +249,12 @@ class ApplicationInstaller
$sAdminLanguage = $aAdminParams['language'];
$sLanguage = $this->oParams->Get('language');
$aSelectedModules = $this->oParams->Get('selected_modules', array());
$sDataModelVersion = $this->oParams->Get('datamodel_version', '0.0.0');
$bOldAddon = $this->oParams->Get('old_addon', false);
$sSourceDir = $this->oParams->Get('source_dir', '');
self::AfterDBCreate($sMode, $sTargetDir, $sDBServer, $sDBUser, $sDBPwd, $sDBName, $sDBPrefix, $sAdminUser, $sAdminPwd, $sAdminLanguage, $sLanguage, $aSelectedModules, $sTargetEnvironment);
self::AfterDBCreate($sMode, $sTargetDir, $sDBServer, $sDBUser, $sDBPwd, $sDBName, $sDBPrefix, $sAdminUser,
$sAdminPwd, $sAdminLanguage, $sLanguage, $aSelectedModules, $sTargetEnvironment, $bOldAddon, $sDataModelVersion, $sSourceDir);
$aResult = array(
'status' => self::OK,
@@ -277,8 +283,9 @@ class ApplicationInstaller
$sDBName = $aDBParams['name'];
$sDBPrefix = $aDBParams['prefix'];
$aFiles = $this->oParams->Get('files', array());
$bOldAddon = $this->oParams->Get('old_addon', false);
self::DoLoadFiles($aSelectedModules, $sTargetDir, $sDBServer, $sDBUser, $sDBPwd, $sDBName, $sDBPrefix, $sTargetEnvironment);
self::DoLoadFiles($aSelectedModules, $sTargetDir, $sDBServer, $sDBUser, $sDBPwd, $sDBName, $sDBPrefix, $sTargetEnvironment, $bOldAddon);
$aResult = array(
'status' => self::INFO,
@@ -306,8 +313,11 @@ class ApplicationInstaller
$sUrl = $this->oParams->Get('url', '');
$sLanguage = $this->oParams->Get('language', '');
$aSelectedModules = $this->oParams->Get('selected_modules', array());
$bOldAddon = $this->oParams->Get('old_addon', false);
$sSourceDir = $this->oParams->Get('source_dir', '');
$sPreviousConfigFile = $this->oParams->Get('previous_configuration_file', '');
self::DoCreateConfig($sMode, $sTargetDir, $sDBServer, $sDBUser, $sDBPwd, $sDBName, $sDBPrefix, $sUrl, $sLanguage, $aSelectedModules, $sTargetEnvironment);
self::DoCreateConfig($sMode, $sTargetDir, $sDBServer, $sDBUser, $sDBPwd, $sDBName, $sDBPrefix, $sUrl, $sLanguage, $aSelectedModules, $sTargetEnvironment, $bOldAddon, $sSourceDir, $sPreviousConfigFile);
$aResult = array(
'status' => self::INFO,
@@ -338,6 +348,9 @@ class ApplicationInstaller
'next-step-label' => '',
'percentage-completed' => 100,
);
SetupPage::log_error('An exception occurred: '.$e->getMessage());
SetupPage::log("Stack trace:\n".$e->getTraceAsString());
}
return $aResult;
}
@@ -374,7 +387,7 @@ class ApplicationInstaller
}
protected static function DoCompile($aSelectedModules, $sSourceDir, $sTargetDir, $sWorkspaceDir = '', $bUseSymbolicLinks = false)
protected static function DoCompile($aSelectedModules, $sSourceDir, $sExtensionDir, $sTargetDir, $sWorkspaceDir = '', $bUseSymbolicLinks = false)
{
SetupPage::log_info("Compiling data model.");
@@ -388,6 +401,7 @@ class ApplicationInstaller
}
$sSourcePath = APPROOT.$sSourceDir;
$sExtensionsPath = APPROOT.$sExtensionDir;
$sTargetPath = APPROOT.$sTargetDir;
if (!is_dir($sSourcePath))
{
@@ -407,7 +421,7 @@ class ApplicationInstaller
}
}
$oFactory = new ModelFactory($sSourcePath);
$oFactory = new ModelFactory(array($sSourcePath, $sExtensionsPath));
$aModules = $oFactory->FindModules();
foreach($aModules as $foo => $oModule)
@@ -441,13 +455,24 @@ class ApplicationInstaller
}
else
{
$oMFCompiler = new MFCompiler($oFactory, $sSourcePath);
$oMFCompiler = new MFCompiler($oFactory);
$oMFCompiler->Compile($sTargetPath, null, $bUseSymbolicLinks);
SetupPage::log_info("Data model successfully compiled to '$sTargetPath'.");
}
// Special case to patch a ugly patch in itop-config-mgmt
$sFileToPatch = $sTargetPath.'/itop-config-mgmt-1.0.0/model.itop-config-mgmt.php';
if (file_exists($sFileToPatch))
{
$sContent = file_get_contents($sFileToPatch);
$sContent = str_replace("require_once(APPROOT.'modules/itop-welcome-itil/model.itop-welcome-itil.php');", "//\n// The line below is no longer needed in iTop 2.0 -- patched by the setup program\n// require_once(APPROOT.'modules/itop-welcome-itil/model.itop-welcome-itil.php');", $sContent);
file_put_contents($sFileToPatch, $sContent);
}
}
protected static function DoUpdateDBSchema($sMode, $sModulesDir, $sDBServer, $sDBUser, $sDBPwd, $sDBName, $sDBPrefix, $sTargetEnvironment = '')
protected static function DoUpdateDBSchema($sMode, $sModulesDir, $sDBServer, $sDBUser, $sDBPwd, $sDBName, $sDBPrefix, $sTargetEnvironment = '', $bOldAddon = false)
{
SetupPage::log_info("Update Database Schema for environment '$sTargetEnvironment'.");
@@ -461,7 +486,14 @@ class ApplicationInstaller
'db_prefix' => $sDBPrefix,
);
$oConfig->UpdateFromParams($aParamValues, $sModulesDir);
if ($bOldAddon)
{
// Old version of the add-on for backward compatibility with pre-2.0 data models
$oConfig->SetAddons(array(
'user rights' => 'addons/userrights/userrightsprofile.db.class.inc.php',
));
}
$oProductionEnv = new RunTimeEnvironment($sTargetEnvironment);
$oProductionEnv->InitDataModel($oConfig, true); // load data model only
@@ -472,7 +504,7 @@ class ApplicationInstaller
SetupPage::log_info("Database Schema Successfully Updated for environment '$sTargetEnvironment'.");
}
protected static function AfterDBCreate($sMode, $sModulesDir, $sDBServer, $sDBUser, $sDBPwd, $sDBName, $sDBPrefix, $sAdminUser, $sAdminPwd, $sAdminLanguage, $sLanguage, $aSelectedModules, $sTargetEnvironment = '')
protected static function AfterDBCreate($sMode, $sModulesDir, $sDBServer, $sDBUser, $sDBPwd, $sDBName, $sDBPrefix, $sAdminUser, $sAdminPwd, $sAdminLanguage, $sLanguage, $aSelectedModules, $sTargetEnvironment, $bOldAddon, $sDataModelVersion, $sSourceDir)
{
SetupPage::log_info('After Database Creation');
@@ -487,18 +519,27 @@ class ApplicationInstaller
'db_prefix' => $sDBPrefix,
);
$oConfig->UpdateFromParams($aParamValues, $sModulesDir);
if ($bOldAddon)
{
// Old version of the add-on for backward compatibility with pre-2.0 data models
$oConfig->SetAddons(array(
'user rights' => 'addons/userrights/userrightsprofile.db.class.inc.php',
));
}
$oConfig->Set('source_dir', $sSourceDir); // Needed by RecordInstallation below
$oProductionEnv = new RunTimeEnvironment($sTargetEnvironment);
$oProductionEnv->InitDataModel($oConfig, false); // load data model and connect to the database
$oProductionEnv->InitDataModel($oConfig, true); // load data model and connect to the database
self::$bMetaModelStarted = true; // No need to reload the final MetaModel in case the installer runs synchronously
// Perform here additional DB setup... profiles, etc...
//
$aAvailableModules = $oProductionEnv->AnalyzeInstallation(MetaModel::GetConfig(), $sModulesDir);
$aAvailableModules = $oProductionEnv->AnalyzeInstallation(MetaModel::GetConfig(), APPROOT.$sModulesDir);
foreach($aAvailableModules as $sModuleId => $aModule)
{
if (($sModuleId != ROOT_MODULE) && in_array($sModuleId, $aSelectedModules) &&
isset($aAvailableModules[$sModuleId]['installer']) )
isset($aAvailableModules[$sModuleId]['installer']) )
{
$sModuleInstallerClass = $aAvailableModules[$sModuleId]['installer'];
SetupPage::log_info("Calling Module Handler: $sModuleInstallerClass::AfterDatabaseCreation(oConfig, {$aModule['version_db']}, {$aModule['version_code']})");
@@ -555,7 +596,7 @@ class ApplicationInstaller
}
}
if (!$oProductionEnv->RecordInstallation($oConfig, $aSelectedModules, $sModulesDir))
if (!$oProductionEnv->RecordInstallation($oConfig, $sDataModelVersion, $aSelectedModules, $sModulesDir))
{
throw new Exception("Failed to record the installation information");
}
@@ -591,7 +632,7 @@ class ApplicationInstaller
}
}
protected static function DoLoadFiles($aSelectedModules, $sModulesDir, $sDBServer, $sDBUser, $sDBPwd, $sDBName, $sDBPrefix, $sTargetEnvironment = '')
protected static function DoLoadFiles($aSelectedModules, $sModulesDir, $sDBServer, $sDBUser, $sDBPwd, $sDBName, $sDBPrefix, $sTargetEnvironment = '', $bOldAddon = false)
{
$aParamValues = array(
'db_server' => $sDBServer,
@@ -604,7 +645,14 @@ class ApplicationInstaller
$oConfig = new Config();
$oConfig->UpdateFromParams($aParamValues, $sModulesDir);
if ($bOldAddon)
{
// Old version of the add-on for backward compatibility with pre-2.0 data models
$oConfig->SetAddons(array(
'user rights' => 'addons/userrights/userrightsprofile.db.class.inc.php',
));
}
//Load the MetaModel if needed (asynchronous mode)
if (!self::$bMetaModelStarted)
{
@@ -624,7 +672,7 @@ class ApplicationInstaller
$aFiles = array();
$oProductionEnv = new RunTimeEnvironment();
$aAvailableModules = $oProductionEnv->AnalyzeInstallation($oConfig, $sModulesDir);
$aAvailableModules = $oProductionEnv->AnalyzeInstallation($oConfig, APPROOT.$sModulesDir);
foreach($aAvailableModules as $sModuleId => $aModule)
{
if (($sModuleId != ROOT_MODULE))
@@ -658,7 +706,7 @@ class ApplicationInstaller
SetupPage::log_info("ending data load session");
}
protected static function DoCreateConfig($sMode, $sModulesDir, $sDBServer, $sDBUser, $sDBPwd, $sDBName, $sDBPrefix, $sUrl, $sLanguage, $aSelectedModules, $sTargetEnvironment = '')
protected static function DoCreateConfig($sMode, $sModulesDir, $sDBServer, $sDBUser, $sDBPwd, $sDBName, $sDBPrefix, $sUrl, $sLanguage, $aSelectedModules, $sTargetEnvironment, $bOldAddon, $sSourceDir, $sPreviousConfigFile)
{
$aParamValues = array(
'db_server' => $sDBServer,
@@ -668,20 +716,46 @@ class ApplicationInstaller
'new_db_name' => $sDBName,
'db_prefix' => $sDBPrefix,
'application_path' => $sUrl,
'mode' => $sMode,
'language' => $sLanguage,
'selected_modules' => implode(',', $aSelectedModules),
);
$oConfig = new Config();
$bPreserveModuleSettings = false;
if ($sMode == 'upgrade')
{
try
{
$oOldConfig = new Config($sPreviousConfigFile);
$oConfig = clone($oOldConfig);
$bPreserveModuleSettings = true;
}
catch(Exception $e)
{
// In case the previous configuration is corrupted... start with a blank new one
$oConfig = new Config();
}
}
else
{
$oConfig = new Config();
}
// Migration: force utf8_unicode_ci as the collation to make the global search
// NON case sensitive
$oConfig->SetDBCollation('utf8_unicode_ci');
// Final config update: add the modules
$oConfig->UpdateFromParams($aParamValues, $sModulesDir);
$oConfig->UpdateFromParams($aParamValues, $sModulesDir, $bPreserveModuleSettings);
if ($bOldAddon)
{
// Old version of the add-on for backward compatibility with pre-2.0 data models
$oConfig->SetAddons(array(
'user rights' => 'addons/userrights/userrightsprofile.db.class.inc.php',
));
}
$oConfig->Set('source_dir', $sSourceDir);
// Make sure the root configuration directory exists
if (!file_exists(APPCONF))
{
@@ -700,5 +774,7 @@ class ApplicationInstaller
// try to make the final config file read-only
@chmod($sConfigFile, 0444); // Read-only for owner and group, nothing for others
// Ready to go !!
MetaModel::ResetCache($sTargetEnvironment);
}
}

View File

@@ -27,15 +27,14 @@ class DOMFormatException extends Exception
class MFCompiler
{
protected $oFactory;
protected $sSourceDir;
protected $aSourceDirs;
protected $aRootClasses;
protected $aLog;
public function __construct($oModelFactory, $sSourceDir)
public function __construct($oModelFactory)
{
$this->oFactory = $oModelFactory;
$this->sSourceDir = $sSourceDir;
$this->aLog = array();
}
@@ -97,7 +96,7 @@ class MFCompiler
$sModuleVersion = $oModule->GetVersion();
$sModuleRootDir = realpath($oModule->GetRootDir());
$sRelativeDir = substr($sModuleRootDir, strlen($this->sSourceDir) + 1);
$sRelativeDir = basename($sModuleRootDir);
// Push the other module files
SetupUtils::copydir($sModuleRootDir, $sTargetDir.'/'.$sRelativeDir, $bUseSymbolicLinks);
@@ -878,11 +877,12 @@ EOF;
{
$sPHP .= "\nrequire_once('$sIncludeFile'); // Implementation of the class $sParentClass\n";
}
$sFullPath = $this->sSourceDir.'/'.$sModuleRelativeDir.'/'.$sIncludeFile;
if (!file_exists($sFullPath))
{
throw new Exception("Failed to process class '".$oClass->getAttribute('id')."', from '$sModuleRelativeDir'. The required include file: '$sFullPath' does not exist.");
}
//TODO fix this !!!
// $sFullPath = $this->sSourceDir.'/'.$sModuleRelativeDir.'/'.$sIncludeFile;
// if (!file_exists($sFullPath))
// {
// throw new Exception("Failed to process class '".$oClass->getAttribute('id')."', from '$sModuleRelativeDir'. The required include file: '$sFullPath' does not exist.");
// }
}
else
{
@@ -1151,7 +1151,6 @@ EOF;
$sPHP =
<<<EOF
//
// List of constant profiles
// - used by the class URP_Profiles at setup (create/update/delete records)

View File

@@ -30,5 +30,12 @@ require_once(APPROOT.'/setup/setuppage.class.inc.php');
require_once(APPROOT.'/setup/wizardcontroller.class.inc.php');
require_once(APPROOT.'/setup/wizardsteps.class.inc.php');
clearstatcache(); // Make sure we know what we are doing !
// Set a long (at least 4 minutes) execution time for the setup to avoid timeouts during this phase
ini_set('max_execution_time', max(240, ini_get('max_execution_time')));
// While running the setup it is desirable to see any error that may happen
ini_set('display_errors', true);
ini_set('display_startup_errors', true);
$oWizard = new WizardController('WizStepWelcome');
$oWizard->Run();

View File

@@ -1076,7 +1076,7 @@ EOF
*/
public function FindModules($sSubDirectory = '')
{
$aAvailableModules = ModuleDiscovery::GetAvailableModules($this->sRootDir, $sSubDirectory);
$aAvailableModules = ModuleDiscovery::GetAvailableModules($this->sRootDir, array($sSubDirectory));
$aResult = array();
foreach($aAvailableModules as $sId => $aModule)
{

View File

@@ -30,11 +30,8 @@ class ModuleDiscovery
);
// Cache the results and the source directory
// Note that, as class can be declared within the module files, they cannot be loaded twice.
// Then the following assumption is made: within the same execution page, the module
// discovery CANNOT be executed on several different paths
protected static $m_sModulesRoot = null;
// Cache the results and the source directories
protected static $m_aSearchDirs = null;
protected static $m_aModules = array();
// All the entries below are list of file paths relative to the module directory
@@ -164,45 +161,44 @@ class ModuleDiscovery
/**
* 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 sRootDir Application root directory
* @param sSearchDir Directory to search (relative to root dir)
* @param aSearchDirs Array of directories to search (absolute paths)
* @return Hash A big array moduleID => ModuleData
*/
public static function GetAvailableModules($sRootDir, $sSearchDir, $oP = null)
public static function GetAvailableModules($aSearchDirs, $oP = null)
{
$sLookupDir = realpath($sRootDir.'/'.$sSearchDir);
if (self::$m_sModulesRoot != $sLookupDir)
if (self::$m_aSearchDirs != $aSearchDirs)
{
self::ResetCache();
}
if (is_null(self::$m_sModulesRoot))
if (is_null(self::$m_aSearchDirs))
{
// First call
//
if ($sLookupDir == '')
self::$m_aSearchDirs = $aSearchDirs;
// Not in cache, let's scan the disk
foreach($aSearchDirs as $sSearchDir)
{
throw new Exception("Invalid directory '$sRootDir/$sSearchDir'");
$sLookupDir = realpath($sSearchDir);
if ($sLookupDir == '')
{
throw new Exception("Invalid directory '$sSearchDir'");
}
clearstatcache();
self::ListModuleFiles(basename($sSearchDir), dirname($sSearchDir));
}
self::$m_sModulesRoot = $sLookupDir;
clearstatcache();
self::ListModuleFiles($sSearchDir, $sRootDir);
return self::GetModules($oP);
}
else
{
// Reuse the previous results
//
return self::GetModules($oP);
}
}
public static function ResetCache()
{
self::$m_sModulesRoot = null;
self::$m_sModulesRoot = null;
self::$m_aSearchDirs = null;
self::$m_aModules = array();
}
@@ -233,6 +229,8 @@ class ModuleDiscovery
*/
protected static function ListModuleFiles($sRelDir, $sRootDir)
{
static $iDummyClassIndex = 0;
static $aDefinedClasses = array();
$sDirectory = $sRootDir.'/'.$sRelDir;
if ($hDir = opendir($sDirectory))
@@ -253,15 +251,35 @@ class ModuleDiscovery
self::SetModulePath($sRelDir);
try
{
//echo "<p>Loading: $sDirectory/$sFile...</p>\n";
//SetupPage::log_info("Discovered module $sFile");
require($sDirectory.'/'.$sFile); // WARNING require_once will NOT work IIF doing an unattended installation WITH symbolic links
// since datamodel/xxx/module.xxx.php and env-production/xxx/module.xxx.php are actually the same file (= inode)
$sModuleFileContents = file_get_contents($sDirectory.'/'.$sFile);
$sModuleFileContents = str_replace(array('<?php', '?>'), '', $sModuleFileContents);
$sModuleFileContents = str_replace('__FILE__', "'".addslashes($sDirectory.'/'.$sFile)."'", $sModuleFileContents);
preg_match_all('/class ([A-Za-z0-9_]+) extends ([A-Za-z0-9_]+)/', $sModuleFileContents, $aMatches);
//print_r($aMatches);
$idx = 0;
foreach($aMatches[1] as $sClassName)
{
if (class_exists($sClassName))
{
// rename the class inside the code to prevent a "duplicate class" declaration
// and change its parent class as well so that nobody will find it and try to execute it
$sModuleFileContents = str_replace($sClassName.' extends '.$aMatches[2][$idx], $sClassName.'_'.($iDummyClassIndex++).' extends DummyHandler', $sModuleFileContents);
}
$idx++;
}
$bRet = eval($sModuleFileContents);
if ($bRet === false)
{
SetupPage::log_warning("Eval of $sRelDir/$sFile returned false");
}
//echo "<p>Done.</p>\n";
}
catch(Exception $e)
{
// Continue...
SetupPage::log_warning("Eval of $sRelDir/$sFile caused an exception: ".$e->getMessage());
}
}
}
@@ -279,6 +297,41 @@ class ModuleDiscovery
* the declaration of a module invokes SetupWebPage::AddModule()
* whereas the new form is ModuleDiscovery::AddModule()
*/
class SetupWebPage extends ModuleDiscovery{}
class SetupWebPage extends ModuleDiscovery
{
// For backward compatibility with old modules...
public static function log_error($sText)
{
SetupPage::log_error($sText);
}
?>
public static function log_warning($sText)
{
SetupPage::log_warning($sText);
}
public static function log_info($sText)
{
SetupPage::log_info($sText);
}
public static function log_ok($sText)
{
SetupPage::log_ok($sText);
}
public static function log($sText)
{
SetupPage::log($sText);
}
}
/** Ugly patch !!!
* In order to be able to analyse / load several times
* the same module file, we rename the class (to avoid duplicate class definitions)
* and we make the class extends the dummy class below in order to "deactivate" completely
* the class (in case some piece of code enumerate the classes derived from a well known class)
* Note that this will not work if someone enumerates the classes that implement a given interface
*/
class DummyHandler {
}

View File

@@ -32,6 +32,7 @@ define ('MODULE_ACTION_OPTIONAL', 1);
define ('MODULE_ACTION_MANDATORY', 2);
define ('MODULE_ACTION_IMPOSSIBLE', 3);
define ('ROOT_MODULE', '_Root_'); // Convention to store IN MEMORY the name/version of the root module i.e. application
define ('DATAMODEL_MODULE', 'datamodel'); // Convention to store the version of the datamodel
class RunTimeEnvironment
{
@@ -91,7 +92,8 @@ class RunTimeEnvironment
/**
* Analyzes the current installation and the possibilities
*
* @param $oConfig Config Defines the target environment (DB)
* @param Config $oConfig Defines the target environment (DB)
* @param mixed $modulesPath Either a single string or an array of absolute paths
* @return hash Array with the following format:
* array =>
* 'iTop' => array(
@@ -115,7 +117,7 @@ class RunTimeEnvironment
* )
* )
*/
public function AnalyzeInstallation($oConfig, $sModulesRelativePath)
public function AnalyzeInstallation($oConfig, $modulesPath)
{
$aRes = array(
ROOT_MODULE => array(
@@ -126,7 +128,8 @@ class RunTimeEnvironment
)
);
$aModules = ModuleDiscovery::GetAvailableModules(APPROOT, $sModulesRelativePath);
$aDirs = is_array($modulesPath) ? $modulesPath : array($modulesPath);
$aModules = ModuleDiscovery::GetAvailableModules($aDirs);
foreach($aModules as $sModuleId => $aModuleInfo)
{
list($sModuleName, $sModuleVersion) = ModuleDiscovery::GetModuleName($sModuleId);
@@ -137,7 +140,8 @@ class RunTimeEnvironment
if ($sModuleVersion == '')
{
// The version must not be empty (it will be used as a criteria to determine wether a module has been installed or not)
throw new Exception("Missing version for the module: '$sModuleId'");
//throw new Exception("Missing version for the module: '$sModuleId'");
$sModuleVersion = '1.0.0';
}
$sModuleAppVersion = $aModuleInfo['itop_version'];
@@ -225,7 +229,7 @@ class RunTimeEnvironment
//
foreach ($aInstallByModule as $sModuleName => $aModuleDB)
{
if ($sModuleName == ROOT_MODULE) continue; // Skip the main module
if ($sModuleName == ROOT_MODULE) continue; // Skip the main module
if (!array_key_exists($sModuleName, $aRes))
{
@@ -284,7 +288,7 @@ class RunTimeEnvironment
//
$oSourceConfig = new Config(APPCONF.$sSourceEnv.'/'.ITOP_CONFIG_FILE);
$oSourceEnv = new RunTimeEnvironment($sSourceEnv);
$aAvailableModules = $oSourceEnv->AnalyzeInstallation($oSourceConfig, $sSourceDir);
$aAvailableModules = $oSourceEnv->AnalyzeInstallation($oSourceConfig, $sSourceDir); //TODO: use an absolute PATH
// Do load the required modules
//
@@ -419,8 +423,19 @@ class RunTimeEnvironment
return true;
}
public function RecordInstallation(Config $oConfig, $aSelectedModules, $sModulesRelativePath)
public function RecordInstallation(Config $oConfig, $sDataModelVersion, $aSelectedModules, $sModulesRelativePath)
{
// Record datamodel version
$aData = array(
'source_dir' => $oConfig->Get('source_dir'),
);
$oInstallRec = new ModuleInstallation();
$oInstallRec->Set('name', DATAMODEL_MODULE);
$oInstallRec->Set('version', $sDataModelVersion);
$oInstallRec->Set('comment', json_encode($aData, true));
$oInstallRec->Set('parent_id', 0); // root module
$iMainItopRecord = $oInstallRec->DBInsertNoReload();
// Record main installation
$oInstallRec = new ModuleInstallation();
$oInstallRec->Set('name', ITOP_APPLICATION);
@@ -429,9 +444,10 @@ class RunTimeEnvironment
$oInstallRec->Set('parent_id', 0); // root module
$iMainItopRecord = $oInstallRec->DBInsertNoReload();
// Record installed modules
//
$aAvailableModules = $this->AnalyzeInstallation($oConfig, $sModulesRelativePath);
$aAvailableModules = $this->AnalyzeInstallation($oConfig, APPROOT.$sModulesRelativePath);
foreach($aSelectedModules as $sModuleId)
{
$aModuleData = $aAvailableModules[$sModuleId];
@@ -471,6 +487,60 @@ class RunTimeEnvironment
// Database is created, installation has been tracked into it
return true;
}
public function GetApplicationVersion(Config $oConfig)
{
$aResult = false;
try
{
require_once(APPROOT.'/core/cmdbsource.class.inc.php');
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 erroneous information
return false;
}
// Scan the list of installed modules to get the version of the 'ROOT' module which holds the main application version
foreach ($aSelectInstall as $aInstall)
{
$sModuleVersion = $aInstall['version'];
if ($sModuleVersion == '')
{
// Though the version cannot be empty in iTop 2.0, it used to be possible
// therefore we have to put something here or the module will not be considered
// as being installed
$sModuleVersion = '0.0.0';
}
if ($aInstall['parent_id'] == 0)
{
if ($aInstall['name'] == DATAMODEL_MODULE)
{
$aResult['datamodel_version'] = $sModuleVersion;
$aComments = json_decode($aInstall['comment'], true);
if (is_array($aComments))
{
$aResult = array_merge($aResult, $aComments);
}
}
else
{
$aResult['product_name'] = $aInstall['name'];
$aResult['product_version'] = $sModuleVersion;
}
}
}
if (!array_key_exists('datamodel_version', $aResult))
{
// Versions prior to 2.0 did not record the version of the datamodel
// so assume that the datamodel version is equal to the application version
$aResult['datamodel_version'] = $aResult['product_version'];
}
return $aResult;
}
public static function MakeDirSafe($sDir)
{
@@ -501,6 +571,3 @@ class RunTimeEnvironment
SetupPage::log_ok($sText);
}
} // End of class
?>

View File

@@ -304,5 +304,3 @@ h3.clickable.open {
}
}
} // End of class
?>

View File

@@ -657,7 +657,7 @@ function DoCheckDBConnection()
'db_pwd': $("#db_pwd").val(),
'db_name': $("#db_name").val()
}
if (oXHRCheckDB !== null)
if ((oXHRCheckDB != null) && (oXHRCheckDB != undefined))
{
oXHRCheckDB.abort();
oXHRCheckDB = null;
@@ -937,6 +937,21 @@ EOF
{
require_once(APPROOT.'/setup/moduleinstaller.class.inc.php');
$oConfig = new Config();
$sSourceDir = $oWizard->GetParameter('source_dir', '');
if (strpos($sSourceDir, APPROOT) !== false)
{
$sRelativeSourceDir = str_replace(APPROOT, '', $sSourceDir);
}
else if (strpos($sSourceDir, $oWizard->GetParameter('previous_version_dir')) !== false)
{
$sRelativeSourceDir = str_replace($oWizard->GetParameter('previous_version_dir'), '', $sSourceDir);
}
else
{
throw(new Exception('Internal error: AnalyzeInstallation: source_dir is neither under APPROOT nor under previous_installation_dir ???'));
}
$aParamValues = array(
'db_server' => $oWizard->GetParameter('db_server', ''),
@@ -944,16 +959,43 @@ EOF
'db_pwd' => $oWizard->GetParameter('db_pwd', ''),
'db_name' => $oWizard->GetParameter('db_name', ''),
'db_prefix' => $oWizard->GetParameter('db_prefix', ''),
'source_dir' => APPROOT.'datamodel',
'source_dir' => $sRelativeSourceDir,
);
$oConfig->UpdateFromParams($aParamValues, 'datamodel');
$aDirsToScan = array($sSourceDir);
if (is_dir($sSourceDir.'/extensions'))
{
$aDirsToScan[] = $sSourceDir.'/extensions';
}
if (is_dir($oWizard->GetParameter('copy_extensions_from')))
{
$aDirsToScan[] = $oWizard->GetParameter('copy_extensions_from');
}
$oProductionEnv = new RunTimeEnvironment();
$aAvailableModules = $oProductionEnv->AnalyzeInstallation($oConfig, 'datamodel');
$aAvailableModules = $oProductionEnv->AnalyzeInstallation($oConfig, $aDirsToScan);
return $aAvailableModules;
}
public static function GetApplicationVersion($oWizard)
{
require_once(APPROOT.'/setup/moduleinstaller.class.inc.php');
$oConfig = new Config();
$aParamValues = array(
'db_server' => $oWizard->GetParameter('db_server', ''),
'db_user' => $oWizard->GetParameter('db_user', ''),
'db_pwd' => $oWizard->GetParameter('db_pwd', ''),
'db_name' => $oWizard->GetParameter('db_name', ''),
'db_prefix' => $oWizard->GetParameter('db_prefix', ''),
'source_dir' => '',
);
$oConfig->UpdateFromParams($aParamValues, 'datamodel');
$oProductionEnv = new RunTimeEnvironment();
return $oProductionEnv->GetApplicationVersion($oConfig);
}
/**
* Checks if the content of a directory matches the given manifest
* @param string $sBaseDir Path to the root directory of iTop
@@ -964,6 +1006,7 @@ EOF
*/
public static function CheckDirAgainstManifest($sBaseDir, $sSourceDir, $aManifest, $aExcludeNames = array('.svn'), $aResult = null)
{
//echo "CheckDirAgainstManifest($sBaseDir, $sSourceDir ...)\n";
if ($aResult === null)
{
$aResult = array('added' => array(), 'removed' => array(), 'modified' => array());
@@ -990,10 +1033,13 @@ EOF
}
}
//echo "The manifest contains ".count($aDirManifest)." files for the directory '$sSourceDir' (and below)\n";
// Read the content of the directory
foreach(glob($sBaseDir.'/'.$sSourceDir .'/*') as $sFilePath)
{
$sFile = basename($sFilePath);
//echo "Checking $sFile ($sFilePath)\n";
if (in_array(basename($sFile), $aExcludeNames)) continue;
@@ -1023,6 +1069,7 @@ EOF
if ($sMD5 != $aDirManifest[$sFile]['md5'])
{
$aResult['modified'][$sSourceDir.'/'.$sFile] = 'Content modified (MD5 checksums differ).';
//echo $sSourceDir.'/'.$sFile." modified ($sMD5 == {$aDirManifest[$sFile]['md5']})\n";
}
//else
//{
@@ -1051,9 +1098,10 @@ EOF
$aManifest[] = array('path' => (string)$oFileInfo->path, 'size' => (int)$oFileInfo->size, 'md5' => (string)$oFileInfo->md5);
}
$sBaseDir = preg_replace('|modules/?$|', '', $sBaseDir);
$aResults = self::CheckDirAgainstManifest($sBaseDir, 'modules', $aManifest);
// echo "<pre>Comparison of ".dirname($sBaseDir)."/modules:\n".print_r($aResults, true)."</pre>";
// echo "<pre>Comparison of ".dirname($sBaseDir)."/modules against $sManifestFile:\n".print_r($aResults, true)."</pre>";
return $aResults;
}
@@ -1084,7 +1132,7 @@ EOF
$aResults = array('added' => array(), 'removed' => array(), 'modified' => array());
foreach(array('addons', 'core', 'dictionaries', 'js', 'application', 'css', 'pages', 'synchro', 'webservices') as $sDir)
{
$aTmp = self::CheckDirAgainstManifest($sBaseDir, 'portal', $aManifest);
$aTmp = self::CheckDirAgainstManifest($sBaseDir, $sDir, $aManifest);
$aResults['added'] = array_merge($aResults['added'], $aTmp['added']);
$aResults['modified'] = array_merge($aResults['modified'], $aTmp['modified']);
$aResults['removed'] = array_merge($aResults['removed'], $aTmp['removed']);
@@ -1094,4 +1142,99 @@ EOF
return $aResults;
}
public static function CheckVersion($sInstalledVersion, $sSourceDir)
{
$sManifestFilePath = self::GetVersionManifest($sInstalledVersion);
if ($sSourceDir != '')
{
if (file_exists($sManifestFilePath))
{
$aDMchanges = self::CheckDataModelFiles($sManifestFilePath, $sSourceDir);
//$aPortalChanges = self::CheckPortalFiles($sManifestFilePath, $sSourceDir);
//$aCodeChanges = self::CheckApplicationFiles($sManifestFilePath, $sSourceDir);
//echo("Changes detected compared to $sInstalledVersion:<br/>DataModel:<br/><pre>".print_r($aDMchanges, true)."</pre>");
//echo("Changes detected compared to $sInstalledVersion:<br/>DataModel:<br/><pre>".print_r($aDMchanges, true)."</pre><br/>Portal:<br/><pre>".print_r($aPortalChanges, true)."</pre><br/>Code:<br/><pre>".print_r($aCodeChanges, true)."</pre>");
return $aDMchanges;
}
else
{
return false;
}
}
else
{
throw(new Exception("Cannot check version '$sInstalledVersion', no source directory provided to check the files."));
}
}
public static function GetVersionManifest($sInstalledVersion)
{
if (preg_match('/^([0-9]+)\./', $sInstalledVersion, $aMatches))
{
return APPROOT.'datamodels/'.$aMatches[1].'.x/manifest-'.$sInstalledVersion.'.xml';
}
return false;
}
public static function CheckWritableDirs($aWritableDirs)
{
$aNonWritableDirs = array();
foreach($aWritableDirs as $sDir)
{
$sFullPath = APPROOT.$sDir;
if (is_dir($sFullPath) && !is_writable($sFullPath))
{
$aNonWritableDirs[APPROOT.$sDir] = new CheckResult(CheckResult::ERROR, "The directory '".APPROOT.$sDir."' exists but is not writable for the application.");
}
else if (file_exists($sFullPath) && !is_dir($sFullPath))
{
$aNonWritableDirs[APPROOT.$sDir] = new CheckResult(CheckResult::ERROR, "A file with the same name as '".APPROOT.$sDir."' exists.");
}
else if (!is_dir($sFullPath) && !is_writable(APPROOT))
{
$aNonWritableDirs[APPROOT] = new CheckResult(CheckResult::ERROR, "The directory '".APPROOT."' is not writable, the application cannot create the directory '$sDir' inside it.");
}
}
return $aNonWritableDirs;
}
public static function GetLatestDataModelDir()
{
$sBaseDir = APPROOT.'datamodels';
$aDirs = glob($sBaseDir.'/*', GLOB_MARK | GLOB_ONLYDIR);
if ($aDirs !== false)
{
sort($aDirs);
return array_pop($aDirs);
}
return false;
}
public static function GetCompatibleDataModelDir($sInstalledVersion)
{
if (preg_match('/^([0-9]+)\./', $sInstalledVersion, $aMatches))
{
$sMajorVersion = $aMatches[1];
$sDir = APPROOT.'datamodels/'.$sMajorVersion.'.x/';
if (is_dir($sDir))
{
return $sDir;
}
}
return false;
}
static public function GetDataModelVersion($sDatamodelDir)
{
$sVersionFile = $sDatamodelDir.'version.xml';
if (file_exists($sVersionFile))
{
$oParams = new XMLParameters($sVersionFile);
return $oParams->Get('version');
}
return false;
}
}

View File

@@ -164,6 +164,23 @@ class WizardController
protected function DisplayStep(WizardStep $oStep)
{
$oPage = new SetupPage($oStep->GetTitle());
if ($oStep->RequiresWritableConfig())
{
$sConfigFile = utils::GetConfigFilePath();
if (file_exists($sConfigFile))
{
// The configuration file already exists
if (!is_writable($sConfigFile))
{
$oP = new SetupPage('Installation Cannot Continue');
$oP->add("<h2>Fatal error</h2>\n");
$oP->error("<b>Error:</b> the configuration file '".$sConfigFile."' already exists and cannot be overwritten.");
$oP->p("The wizard cannot modify the configuration file for you. If you want to upgrade ".ITOP_APPLICATION.", make sure that the file '<b>".realpath($sConfigFile)."</b>' can be modified by the web server.");
$oP->output();
return;
}
}
}
$oPage->add_linked_script('../setup/setup.js');
$oPage->add_script("function CanMoveForward()\n{\n".$oStep->JSCanMoveForward()."\n}\n");
$oPage->add_script("function CanMoveBackward()\n{\n".$oStep->JSCanMoveBackward()."\n}\n");
@@ -432,6 +449,15 @@ abstract class WizardStep
return 'return true;';
}
/**
* Tells whether this step of the wizard requires that the configuration file be writable
* @return bool True if the wizard will possibly need to modify the configuration at some point
*/
public function RequiresWritableConfig()
{
return true;
}
/**
* Overload this function to implement asynchronous action(s) (AJAX)
* @param string $sCode The code of the action (if several actions need to be distinguished)

File diff suppressed because it is too large Load Diff