diff --git a/datamodels/2.x/itop-hub-connector/launch.php b/datamodels/2.x/itop-hub-connector/launch.php index 8797cf38f..ffa20366b 100644 --- a/datamodels/2.x/itop-hub-connector/launch.php +++ b/datamodels/2.x/itop-hub-connector/launch.php @@ -186,9 +186,7 @@ function collect_configuration() // iTop modules $oConfig = MetaModel::GetConfig(); - $sLatestInstallationDate = CMDBSource::QueryToScalar("SELECT max(installed) FROM ".$oConfig->Get('db_subname')."priv_module_install"); - // Get the latest installed modules, without the "root" ones (iTop version and datamodel version) - $aInstalledModules = CMDBSource::QueryToArray("SELECT * FROM ".$oConfig->Get('db_subname')."priv_module_install WHERE installed = '".$sLatestInstallationDate."' AND parent_id != 0"); + $aInstalledModules = ModuleInstallationService::GetInstance()->ReadFromDB($oConfig); foreach ($aInstalledModules as $aDBInfo) { $aConfiguration['itop_modules'][$aDBInfo['name']] = $aDBInfo['version']; diff --git a/setup/AnalyzeInstallation.php b/setup/AnalyzeInstallation.php index 192d3a7e0..96b902ce5 100644 --- a/setup/AnalyzeInstallation.php +++ b/setup/AnalyzeInstallation.php @@ -96,7 +96,7 @@ class AnalyzeInstallation $aRes[$sModuleName] = $aModuleInfo; } - $aCurrentlyInstalledModules = ModuleInstallationService::GetInstance()->ReadFromDB($oConfig); + $aCurrentlyInstalledModules = ModuleInstallationService::GetInstance()->ReadComputeInstalledModules($oConfig); // Adjust the list of proposed modules foreach ($aCurrentlyInstalledModules as $sModuleName => $aModuleDB) { diff --git a/setup/ModuleInstallationService.php b/setup/ModuleInstallationService.php index 9817fdb67..259f884ed 100644 --- a/setup/ModuleInstallationService.php +++ b/setup/ModuleInstallationService.php @@ -23,34 +23,16 @@ class ModuleInstallationService } private ?array $aSelectInstall = null; - public function ReadFromDB(?Config $oConfig): array + + /** +* @param \Config|null $oConfig +* @return array + */ + public function ReadComputeInstalledModules(?Config $oConfig): array { + $aSelectInstall = []; try { - $aSelectInstall = []; - if (! is_null($oConfig)) { - if (! is_null($this->aSelectInstall)) { - //test only - $aSelectInstall = $this->aSelectInstall; - } else { - CMDBSource::InitFromConfig($oConfig); - - //read db module installations - $aSelectInstallOld = CMDBSource::QueryToArray("SELECT * FROM ".$oConfig->Get('db_subname')."priv_module_install"); - //file_put_contents(APPROOT."/tests/php-unit-tests/unitary-tests/setup/ressources/priv_modules.json", json_encode($aSelectInstallOld, JSON_PRETTY_PRINT)); - - $iRootId = CMDBSource::QueryToScalar("SELECT max(parent_id) FROM ".$oConfig->Get('db_subname')."priv_module_install"); - $sDbSubName = $oConfig->Get('db_subname'); - // Get the latest installed modules, without the "root" ones (iTop version and datamodel version) - $sSQL = <<ReadFromDB($oConfig); } catch (MySQLException $e) { // No database or erroneous information } @@ -58,24 +40,69 @@ SQL; return $this->ComputeInstalledModules($aSelectInstall); } - private function ComputeInstalledModulesLegacy(array $aSelectInstall): array + /** +* @param \Config|null $oConfig +* @return array +* @throws \MySQLException +* @throws \MySQLQueryHasNoResultException + */ + public function ReadFromDB(?Config $oConfig): array { - $aInstallByModule = []; // array of => array ('installed' => timestamp, 'version' => ) - $iRootId = 0; - foreach ($aSelectInstall as $aInstall) { - if (($aInstall['parent_id'] == 0) && ($aInstall['name'] != 'datamodel')) { - // Root module, what is its ID ? - $iId = (int) $aInstall['id']; - if ($iId > $iRootId) { - $iRootId = $iId; - } - } + if (is_null($oConfig)) { + return []; } + if (! is_null($this->aSelectInstall)) { + //test only + return $this->aSelectInstall; + } + + CMDBSource::InitFromConfig($oConfig); + //read db module installations + $tableWithPrefix = $this->GetTableWithPrefix($oConfig); + $iRootId = CMDBSource::QueryToScalar("SELECT max(parent_id) FROM $tableWithPrefix"); + // Get the latest installed modules, without the "root" ones (iTop version and datamodel version) + $sSQL = <<Get('db_subname'); + if (utils::IsNullOrEmptyString($sPrefix)) { + return "priv_module_install"; + } + + return "{$sPrefix}priv_module_install"; + } + + /** + * @param \Config $oConfig + * + * @return array|false + */ + public function GetApplicationVersion(Config $oConfig) + { + try { + CMDBSource::InitFromConfig($oConfig); + $tableWithPrefix = $this->GetTableWithPrefix($oConfig); + $sSQLQuery = "SELECT * FROM $tableWithPrefix"; + $aSelectInstall = CMDBSource::QueryToArray($sSQLQuery); + } catch (MySQLException $e) { + // No database or erroneous information + $this->log_error('Can not connect to the database: host: '.$oConfig->Get('db_host').', user:'.$oConfig->Get('db_user').', pwd:'.$oConfig->Get('db_pwd').', db name:'.$oConfig->Get('db_name')); + $this->log_error('Exception '.$e->getMessage()); + return false; + } + + $aResult = []; + // Scan the list of installed modules to get the version of the 'ROOT' module which holds the main application version foreach ($aSelectInstall as $aInstall) { - //$aInstall['comment']; // unsused - $iInstalled = strtotime($aInstall['installed']); - $sModuleName = $aInstall['name']; $sModuleVersion = $aInstall['version']; if ($sModuleVersion == '') { // Though the version cannot be empty in iTop 2.0, it used to be possible @@ -85,27 +112,25 @@ SQL; } if ($aInstall['parent_id'] == 0) { - $sModuleName = ROOT_MODULE; - } elseif ($aInstall['parent_id'] != $iRootId) { - // Skip all modules belonging to previous installations - continue; - } - - if (array_key_exists($sModuleName, $aInstallByModule)) { - if ($iInstalled < $aInstallByModule[$sModuleName]['installed']) { - continue; + 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 ($aInstall['parent_id'] == 0) { - $aInstallByModule[$sModuleName]['installed_version'] = $sModuleVersion; - } - - $aInstallByModule[$sModuleName]['installed'] = $iInstalled; - $aInstallByModule[$sModuleName]['version'] = $sModuleVersion; } - - return $aInstallByModule; + 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']; + } + $this->log_info("GetApplicationVersion returns: product_name: ".$aResult['product_name'].', product_version: '.$aResult['product_version']); + return empty($aResult) ? false : $aResult; } private function ComputeInstalledModules(array $aSelectInstall): array diff --git a/setup/extensionsmap.class.inc.php b/setup/extensionsmap.class.inc.php index 8e35c86b9..a5b95f517 100644 --- a/setup/extensionsmap.class.inc.php +++ b/setup/extensionsmap.class.inc.php @@ -179,7 +179,7 @@ class iTopExtensionsMap foreach ($aExtraDirs as $sDir) { $this->ReadDir($sDir, iTopExtension::SOURCE_REMOTE); } - $this->CheckDependencies($sFromEnvironment); + $this->CheckDependencies(); } /** @@ -190,8 +190,10 @@ class iTopExtensionsMap */ protected function ScanDisk($sEnvironment) { - if (!$this->ReadInstallationWizard(APPROOT.'/datamodels/2.x') && !$this->ReadInstallationWizard(APPROOT.'/datamodels/2.x')) { + if (!$this->ReadInstallationWizard(APPROOT.'/datamodels/2.x')) { + //no installation xml found in 2.x: let's read all extensions in 2.x first if (!$this->ReadDir(APPROOT.'/datamodels/2.x', iTopExtension::SOURCE_WIZARD)) { + //nothing found in 2.x : fallback read in 1.x (flat structure) $this->ReadDir(APPROOT.'/datamodels/1.x', iTopExtension::SOURCE_WIZARD); } } @@ -387,19 +389,15 @@ class iTopExtensionsMap // to this extension $sModuleId = $aModuleInfo[ModuleFileReader::MODULE_INFO_ID]; list($sModuleName, $sModuleVersion) = ModuleDiscovery::GetModuleName($sModuleId); - if ($sModuleVersion == '') { - // Provide a default module version since version is mandatory when recording ExtensionInstallation - $sModuleVersion = '0.0.1'; - } $aModuleInfo[ModuleFileReader::MODULE_INFO_CONFIG]['uninstallable'] ??= 'yes'; - if (($sParentExtensionId !== null) && (array_key_exists($sParentExtensionId, $this->aExtensions)) && ($this->aExtensions[$sParentExtensionId] instanceof iTopExtension)) { - // Already inside an extension, let's add this module the list of modules belonging to this extension - $this->aExtensions[$sParentExtensionId]->aModules[] = $sModuleName; - $this->aExtensions[$sParentExtensionId]->aModuleVersion[$sModuleName] = $sModuleVersion; - $this->aExtensions[$sParentExtensionId]->aModuleInfo[$sModuleName] = $aModuleInfo[ModuleFileReader::MODULE_INFO_CONFIG]; - } else { - // Not already inside a folder containing an 'extension.xml' file + $oExtension = null; + if ($sParentExtensionId !== null) { + $oExtension = $this->aExtensions[$sParentExtensionId] ?? null; + } + + if (is_null($oExtension)) { + // Not already inside an folder containing an 'extension.xml' file // Ignore non-visible modules and auto-select ones, since these are never prompted // as a choice to the end-user @@ -423,6 +421,13 @@ class iTopExtensionsMap $oExtension->sSourceDir = $sSearchDir; $oExtension->bVisible = $bVisible; $this->AddExtension($oExtension); + } else { + $oExtension->aModules[] = $sModuleName; + $oExtension->aModuleVersion[$sModuleName] = $sModuleVersion; + $oExtension->aModuleInfo[$sModuleName] = $aModuleInfo[ModuleFileReader::MODULE_INFO_CONFIG]; + + $this->aExtensions[$sParentExtensionId] = $oExtension; + $this->aExtensionsByCode[$oExtension->sCode] = $oExtension; } closedir($hDir); @@ -444,10 +449,9 @@ class iTopExtensionsMap /** * Check if some extension contains a module with missing dependencies... * If so, populate the aMissingDepenencies array - * @param string $sFromEnvironment * @return void */ - protected function CheckDependencies($sFromEnvironment) + protected function CheckDependencies() { $aSearchDirs = []; @@ -459,7 +463,7 @@ class iTopExtensionsMap $aSearchDirs = array_merge($aSearchDirs, $this->aScannedDirs); try { - $aAllModules = ModuleDiscovery::GetAvailableModules($aSearchDirs, true); + ModuleDiscovery::GetAvailableModules($aSearchDirs, true); } catch (MissingDependencyException $e) { // Some modules have missing dependencies // Let's check what is the impact at the "extensions" level @@ -629,8 +633,6 @@ class iTopExtensionsMap */ public function ModuleIsChosenAsPartOfAnExtension($sModuleNameToFind, $sInSourceOnly = iTopExtension::SOURCE_REMOTE) { - $bChosen = false; - foreach ($this->GetAllExtensions() as $oExtension) { if (($oExtension->sSource == $sInSourceOnly) && ($oExtension->bMarkedAsChosen == true) && diff --git a/setup/runtimeenv.class.inc.php b/setup/runtimeenv.class.inc.php index 658db72d7..705e3641a 100644 --- a/setup/runtimeenv.class.inc.php +++ b/setup/runtimeenv.class.inc.php @@ -248,8 +248,6 @@ class RunTimeEnvironment $aExtraDirs = $this->GetExtraDirsToScan($aDirsToCompile); $aDirsToCompile = array_merge($aDirsToCompile, $aExtraDirs); - $aRet = []; - // Determine the installed modules and extensions // $oSourceConfig = new Config(APPCONF.$sSourceEnv.'/'.ITOP_CONFIG_FILE); @@ -290,7 +288,6 @@ class RunTimeEnvironment $aModules = $oFactory->FindModules(); foreach ($aModules as $oModule) { $sModule = $oModule->GetName(); - $sModuleRootDir = $oModule->GetRootDir(); $bIsExtra = $this->GetExtensionMap()->ModuleIsChosenAsPartOfAnExtension($sModule, iTopExtension::SOURCE_REMOTE); if (array_key_exists($sModule, $aAvailableModules)) { if (($aAvailableModules[$sModule]['installed_version'] != '') || $bIsExtra && !$oModule->IsAutoSelect()) { //Extra modules are always unless they are 'AutoSelect' @@ -628,9 +625,7 @@ class RunTimeEnvironment public function GetApplicationVersion(Config $oConfig) { try { - CMDBSource::InitFromConfig($oConfig); - $sSQLQuery = "SELECT * FROM ".$oConfig->Get('db_subname')."priv_module_install"; - $aSelectInstall = CMDBSource::QueryToArray($sSQLQuery); + $aSelectInstall = ModuleInstallationService::GetInstance()->ReadFromDB($oConfig); } catch (MySQLException $e) { // No database or erroneous information $this->log_error('Can not connect to the database: host: '.$oConfig->Get('db_host').', user:'.$oConfig->Get('db_user').', pwd:'.$oConfig->Get('db_pwd').', db name:'.$oConfig->Get('db_name'));