From a62c1946a687619b7f0c067ea326448968d51e5a Mon Sep 17 00:00:00 2001 From: Romain Quetiez Date: Thu, 16 May 2013 08:54:50 +0000 Subject: [PATCH] #721 Unmet dependencies not detected SVN:trunk[2739] --- setup/modulediscovery.class.inc.php | 46 +++++++++++++------------ setup/runtimeenv.class.inc.php | 6 ++-- setup/setuputils.class.inc.php | 36 ++++++++++++++++++-- setup/wizardsteps.class.inc.php | 53 +++++++++++++++++++++++++++-- 4 files changed, 114 insertions(+), 27 deletions(-) diff --git a/setup/modulediscovery.class.inc.php b/setup/modulediscovery.class.inc.php index 14c1a79d0..c8a7924bd 100644 --- a/setup/modulediscovery.class.inc.php +++ b/setup/modulediscovery.class.inc.php @@ -23,6 +23,10 @@ * @license http://opensource.org/licenses/AGPL-3.0 */ +class MissingDependencyException extends Exception +{ +} + class ModuleDiscovery { static $m_aModuleArgs = array( @@ -106,13 +110,22 @@ class ModuleDiscovery } } - protected static function GetModules($oP = null) + /** + * + * @param bool $bAbortOnMissingDependency ... + * @param hash $aModulesToLoad List of modules to search for, defaults to all if ommitted + */ + protected static function GetModules($bAbortOnMissingDependency = false, $aModulesToLoad = null) { // Order the modules to take into account their inter-dependencies $aDependencies = array(); foreach(self::$m_aModules as $sId => $aModule) { - $aDependencies[$sId] = $aModule['dependencies']; + list($sModuleName, $sModuleVersion) = self::GetModuleName($sId); + if (is_null($aModulesToLoad) || in_array($sModuleName, $aModulesToLoad)) + { + $aDependencies[$sId] = $aModule['dependencies']; + } } ksort($aDependencies); $aOrderedModules = array(); @@ -137,27 +150,16 @@ class ModuleDiscovery } $iLoopCount++; } - if (count($aDependencies) >0) + if ($bAbortOnMissingDependency && count($aDependencies) > 0) { - $sHtml = "\n"; - if ($oP instanceof SetupPage) - { - $oP->warning($sHtml); // used in the context of the installation - } - elseif (class_exists('SetupPage')) - { - SetupPage::log_warning($sHtml); // used in the context of ? - } - else - { - echo $sHtml; // used in the context of the compiler + $aModuleDeps[] = "{$aModule['label']} (id: $sId) depends on ".implode(' + ', $aDeps); } + $sMessage = "The following modules have unmet dependencies: ".implode(', ', $aModuleDeps); + throw new MissingDependencyException($sMessage); } // Return the ordered list, so that the dependencies are met... $aResult = array(); @@ -207,9 +209,11 @@ 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 aSearchDirs Array of directories to search (absolute paths) + * @param bool $bAbortOnMissingDependency ... + * @param hash $aModulesToLoad List of modules to search for, defaults to all if ommitted * @return Hash A big array moduleID => ModuleData */ - public static function GetAvailableModules($aSearchDirs, $oP = null) + public static function GetAvailableModules($aSearchDirs, $bAbortOnMissingDependency = false, $aModulesToLoad = null) { if (self::$m_aSearchDirs != $aSearchDirs) { @@ -232,12 +236,12 @@ class ModuleDiscovery clearstatcache(); self::ListModuleFiles(basename($sSearchDir), dirname($sSearchDir)); } - return self::GetModules($oP); + return self::GetModules($bAbortOnMissingDependency, $aModulesToLoad); } else { // Reuse the previous results - return self::GetModules($oP); + return self::GetModules($bAbortOnMissingDependency, $aModulesToLoad); } } diff --git a/setup/runtimeenv.class.inc.php b/setup/runtimeenv.class.inc.php index bb78849bb..b11a521cc 100644 --- a/setup/runtimeenv.class.inc.php +++ b/setup/runtimeenv.class.inc.php @@ -95,6 +95,8 @@ class RunTimeEnvironment * * @param Config $oConfig Defines the target environment (DB) * @param mixed $modulesPath Either a single string or an array of absolute paths + * @param bool $bAbortOnMissingDependency ... + * @param hash $aModulesToLoad List of modules to search for, defaults to all if ommitted * @return hash Array with the following format: * array => * 'iTop' => array( @@ -118,7 +120,7 @@ class RunTimeEnvironment * ) * ) */ - public function AnalyzeInstallation($oConfig, $modulesPath) + public function AnalyzeInstallation($oConfig, $modulesPath, $bAbortOnMissingDependency = false, $aModulesToLoad = null) { $aRes = array( ROOT_MODULE => array( @@ -130,7 +132,7 @@ class RunTimeEnvironment ); $aDirs = is_array($modulesPath) ? $modulesPath : array($modulesPath); - $aModules = ModuleDiscovery::GetAvailableModules($aDirs); + $aModules = ModuleDiscovery::GetAvailableModules($aDirs, $bAbortOnMissingDependency, $aModulesToLoad); foreach($aModules as $sModuleId => $aModuleInfo) { list($sModuleName, $sModuleVersion) = ModuleDiscovery::GetModuleName($sModuleId); diff --git a/setup/setuputils.class.inc.php b/setup/setuputils.class.inc.php index d788e49a2..53a530497 100644 --- a/setup/setuputils.class.inc.php +++ b/setup/setuputils.class.inc.php @@ -336,6 +336,33 @@ class SetupUtils return $aResult; } + /** + * Check that the selected modules meet their dependencies + */ + static function CheckSelectedModules($sSourceDir, $sExtensionDir, $aSelectedModules) + { + $aResult = array(); + SetupPage::log('Info - CheckSelectedModules'); + + $aDirsToScan = array(APPROOT.$sSourceDir); + $sExtensionsPath = APPROOT.$sExtensionDir; + if (is_dir($sExtensionsPath)) + { + // if the extensions dir exists, scan it for additional modules as well + $aDirsToScan[] = $sExtensionsPath; + } + require_once(APPROOT.'setup/modulediscovery.class.inc.php'); + try + { + ModuleDiscovery::GetAvailableModules($aDirsToScan, true, $aSelectedModules); + } + catch(MissingDependencyException $e) + { + $aResult[] = new CheckResult(CheckResult::ERROR, $e->getMessage()); + } + return $aResult; + } + /** * Check that the backup could be executed * @param Page $oP The page used only for its 'log' method @@ -1008,7 +1035,12 @@ EOF return $sHtml; } - public static function AnalyzeInstallation($oWizard) + /** + * + * @param bool $bAbortOnMissingDependency ... + * @param array $aModulesToLoad List of modules to search for, defaults to all if ommitted + */ + public static function AnalyzeInstallation($oWizard, $bAbortOnMissingDependency = false, $aModulesToLoad = null) { require_once(APPROOT.'/setup/moduleinstaller.class.inc.php'); $oConfig = new Config(); @@ -1048,7 +1080,7 @@ EOF $aDirsToScan[] = $oWizard->GetParameter('copy_extensions_from'); } $oProductionEnv = new RunTimeEnvironment(); - $aAvailableModules = $oProductionEnv->AnalyzeInstallation($oConfig, $aDirsToScan); + $aAvailableModules = $oProductionEnv->AnalyzeInstallation($oConfig, $aDirsToScan, $bAbortOnMissingDependency, $aModulesToLoad); return $aAvailableModules; } diff --git a/setup/wizardsteps.class.inc.php b/setup/wizardsteps.class.inc.php index 8383cd698..6738b4887 100644 --- a/setup/wizardsteps.class.inc.php +++ b/setup/wizardsteps.class.inc.php @@ -1095,6 +1095,16 @@ class WizStepModulesChoice extends WizardStep protected function DisplayStep($oPage) { + // Sanity check (not stopper, to let developpers go further...) + try + { + SetupUtils::AnalyzeInstallation($this->oWizard, true); + } + catch(MissingDependencyException $e) + { + $oPage->warning($e->getMessage()); + } + $this->bUpgrade = ($this->oWizard->GetParameter('install_mode') != 'install'); $aStepInfo = $this->GetStepInfo(); $oPage->add_style("div.choice { margin: 0.5em;}"); @@ -1657,6 +1667,28 @@ EOF */ class WizStepSummary extends WizardStep { + protected $bDependencyCheck = null; + protected $sDependencyIssue = null; + + protected function CheckDependencies() + { + if (is_null($this->bDependencyCheck)) + { + $aSelectedModules = json_decode($this->oWizard->GetParameter('selected_modules'), true); + $this->bDependencyCheck = true; + try + { + SetupUtils::AnalyzeInstallation($this->oWizard, true, $aSelectedModules); + } + catch(MissingDependencyException $e) + { + $this->bDependencyCheck = false; + $this->sDependencyIssue = $e->getMessage(); + } + } + return $this->bDependencyCheck; + } + public function GetTitle() { $sMode = $this->oWizard->GetParameter('mode', 'install'); @@ -1684,7 +1716,19 @@ class WizStepSummary extends WizardStep { return ' Install ! '; } - + + public function CanMoveForward() + { + if ($this->CheckDependencies()) + { + return true; + } + else + { + return false; + } + } + public function ProcessParams($bMoveForward = true) { return array('class' => 'WizStepDone', 'state' => ''); @@ -1736,7 +1780,7 @@ class WizStepSummary extends WizardStep } EOF ); - + $aInstallParams = $this->BuildConfig(); $sMode = $aInstallParams['mode']; @@ -1847,6 +1891,11 @@ EOF $sJSONData = json_encode($aInstallParams); $oPage->add(''); + if (!$this->CheckDependencies()) + { + $oPage->error($this->sDependencyIssue); + } + $oPage->add_ready_script( <<