mirror of
https://github.com/Combodo/iTop.git
synced 2026-02-12 23:14:18 +01:00
N°8760 - be able to list modules based on extension choices
refactoring: move some classes in a moduleinstallation folder (coming namespace)
This commit is contained in:
@@ -16,5 +16,5 @@ require_once(APPROOT.'/application/audit.category.class.inc.php');
|
||||
require_once(APPROOT.'/application/audit.domain.class.inc.php');
|
||||
require_once(APPROOT.'/application/audit.rule.class.inc.php');
|
||||
require_once(APPROOT.'/application/query.class.inc.php');
|
||||
require_once(APPROOT.'/setup/moduleinstallation.class.inc.php');
|
||||
require_once(APPROOT.'/setup/moduleinstallation/moduleinstallation.class.inc.php');
|
||||
require_once(APPROOT.'/application/utils.inc.php');
|
||||
|
||||
@@ -24,7 +24,7 @@ MetaModel::IncludeModule('application/user.dashboard.class.inc.php');
|
||||
MetaModel::IncludeModule('application/audit.rule.class.inc.php');
|
||||
MetaModel::IncludeModule('application/audit.domain.class.inc.php');
|
||||
MetaModel::IncludeModule('application/query.class.inc.php');
|
||||
MetaModel::IncludeModule('setup/moduleinstallation.class.inc.php');
|
||||
MetaModel::IncludeModule('setup/moduleinstallation/moduleinstallation.class.inc.php');
|
||||
|
||||
MetaModel::IncludeModule('core/event.class.inc.php');
|
||||
MetaModel::IncludeModule('core/action.class.inc.php');
|
||||
|
||||
@@ -61,7 +61,7 @@ class DependencyExpression
|
||||
}
|
||||
}
|
||||
|
||||
private static function GetPhpExpressionEvaluator(): PhpExpressionEvaluator
|
||||
public static function GetPhpExpressionEvaluator(): PhpExpressionEvaluator
|
||||
{
|
||||
if (!isset(self::$oPhpExpressionEvaluator)) {
|
||||
self::$oPhpExpressionEvaluator = new PhpExpressionEvaluator([], RunTimeEnvironment::STATIC_CALL_AUTOSELECT_WHITELIST);
|
||||
|
||||
@@ -253,79 +253,7 @@ class ModuleDiscovery
|
||||
self::$m_aRemovedExtensions = $aRemovedExtension;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<\iTopExtension> $aExtensions
|
||||
* @param string $sModuleName
|
||||
* @param string $sModuleVersion
|
||||
* @param array $aModuleInfo
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private static function IsModuleInExtensionList(array $aExtensions, string $sModuleName, string $sModuleVersion, array $aModuleInfo): bool
|
||||
{
|
||||
if (count($aExtensions) === 0) {
|
||||
return false;
|
||||
}
|
||||
$aNonMatchingPaths = [];
|
||||
$sModuleFilePath = $aModuleInfo[ModuleFileReader::MODULE_FILE_PATH];
|
||||
|
||||
/** @var \iTopExtension $oExtension */
|
||||
foreach ($aExtensions as $oExtension) {
|
||||
$sCurrentVersion = $oExtension->aModuleVersion[$sModuleName] ?? null;
|
||||
if (is_null($sCurrentVersion)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($sModuleVersion !== $sCurrentVersion) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$aCurrentModuleInfo = $oExtension->aModuleInfo[$sModuleName] ?? null;
|
||||
if (is_null($aCurrentModuleInfo)) {
|
||||
SetupLog::Warning("Missing $sModuleName in ".$oExtension->sLabel.". it should not happen");
|
||||
continue;
|
||||
}
|
||||
|
||||
// use case: same module coming from 2 different extensions
|
||||
// we remove only the one coming from removed extensions
|
||||
$sCurrentModuleFilePath = $aCurrentModuleInfo[ModuleFileReader::MODULE_FILE_PATH];
|
||||
if (realpath($sModuleFilePath) !== realpath($sCurrentModuleFilePath)) {
|
||||
$aNonMatchingPaths[] = $sCurrentModuleFilePath;
|
||||
continue;
|
||||
}
|
||||
|
||||
SetupLog::Info("Module considered as removed", null, ['extension_code' => $oExtension->sCode, 'module_name' => $sModuleName, 'module_version' => $sModuleVersion, ModuleFileReader::MODULE_FILE_PATH => $sCurrentModuleFilePath]);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (count($aNonMatchingPaths) > 0) {
|
||||
//add log for support
|
||||
SetupLog::Debug("Module kept as it came from non removed extensions", null, ['module_name' => $sModuleName, 'module_version' => $sModuleVersion, ModuleFileReader::MODULE_FILE_PATH => $sModuleFilePath, 'non_matching_paths' => $aNonMatchingPaths]);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static function GetPhpExpressionEvaluator(): PhpExpressionEvaluator
|
||||
{
|
||||
if (!isset(self::$oPhpExpressionEvaluator)) {
|
||||
self::$oPhpExpressionEvaluator = new PhpExpressionEvaluator([], RunTimeEnvironment::STATIC_CALL_AUTOSELECT_WHITELIST);
|
||||
}
|
||||
|
||||
return self::$oPhpExpressionEvaluator;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 array $aModulesToLoad List of modules to search for, defaults to all if omitted
|
||||
*
|
||||
* @return array A big array moduleID => ModuleData
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function GetAvailableModules($aSearchDirs, $bAbortOnMissingDependency = false, $aModulesToLoad = null)
|
||||
public static function Init($aSearchDirs): void
|
||||
{
|
||||
if (self::$m_aSearchDirs != $aSearchDirs) {
|
||||
self::ResetCache();
|
||||
@@ -344,13 +272,45 @@ class ModuleDiscovery
|
||||
clearstatcache();
|
||||
self::ListModuleFiles(basename($sSearchDir), dirname($sSearchDir));
|
||||
}
|
||||
return self::GetModules($bAbortOnMissingDependency, $aModulesToLoad);
|
||||
} else {
|
||||
// Reuse the previous results
|
||||
return self::GetModules($bAbortOnMissingDependency, $aModulesToLoad);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* Compute dependency as well (compatibilities & order)
|
||||
*
|
||||
* @param $aSearchDirs array of directories to search (absolute paths)
|
||||
* @param bool $bAbortOnMissingDependency ...
|
||||
* @param array $aModulesToLoad List of modules to search for, defaults to all if omitted
|
||||
*
|
||||
* @return array A big array moduleID => ModuleData
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function GetAvailableModules($aSearchDirs, $bAbortOnMissingDependency = false, $aModulesToLoad = null)
|
||||
{
|
||||
self::Init($aSearchDirs);
|
||||
|
||||
return self::GetModules($bAbortOnMissingDependency, $aModulesToLoad);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* No dependency consideration here.
|
||||
*
|
||||
* @param $aSearchDirs array of directories to search (absolute paths)
|
||||
*
|
||||
* @return array A big array moduleID => ModuleData
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function GetAllModules($aSearchDirs)
|
||||
{
|
||||
self::Init($aSearchDirs);
|
||||
|
||||
return self::$m_aModules;
|
||||
}
|
||||
|
||||
public static function ResetCache()
|
||||
{
|
||||
self::$m_aSearchDirs = null;
|
||||
@@ -419,6 +379,59 @@ class ModuleDiscovery
|
||||
throw new Exception("Data directory (".$sDirectory.") not found or not readable.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<\iTopExtension> $aExtensions
|
||||
* @param string $sModuleName
|
||||
* @param string $sModuleVersion
|
||||
* @param array $aModuleInfo
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private static function IsModuleInExtensionList(array $aExtensions, string $sModuleName, string $sModuleVersion, array $aModuleInfo): bool
|
||||
{
|
||||
if (count($aExtensions) === 0) {
|
||||
return false;
|
||||
}
|
||||
$aNonMatchingPaths = [];
|
||||
$sModuleFilePath = $aModuleInfo[ModuleFileReader::MODULE_FILE_PATH];
|
||||
|
||||
/** @var \iTopExtension $oExtension */
|
||||
foreach ($aExtensions as $oExtension) {
|
||||
$sCurrentVersion = $oExtension->aModuleVersion[$sModuleName] ?? null;
|
||||
if (is_null($sCurrentVersion)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($sModuleVersion !== $sCurrentVersion) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$aCurrentModuleInfo = $oExtension->aModuleInfo[$sModuleName] ?? null;
|
||||
if (is_null($aCurrentModuleInfo)) {
|
||||
SetupLog::Warning("Missing $sModuleName in ".$oExtension->sLabel.". it should not happen");
|
||||
continue;
|
||||
}
|
||||
|
||||
// use case: same module coming from 2 different extensions
|
||||
// we remove only the one coming from removed extensions
|
||||
$sCurrentModuleFilePath = $aCurrentModuleInfo[ModuleFileReader::MODULE_FILE_PATH];
|
||||
if (realpath($sModuleFilePath) !== realpath($sCurrentModuleFilePath)) {
|
||||
$aNonMatchingPaths[] = $sCurrentModuleFilePath;
|
||||
continue;
|
||||
}
|
||||
|
||||
SetupLog::Info("Module considered as removed", null, ['extension_code' => $oExtension->sCode, 'module_name' => $sModuleName, 'module_version' => $sModuleVersion, ModuleFileReader::MODULE_FILE_PATH => $sCurrentModuleFilePath]);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (count($aNonMatchingPaths) > 0) {
|
||||
//add log for support
|
||||
SetupLog::Debug("Module kept as it came from non removed extensions", null, ['module_name' => $sModuleName, 'module_version' => $sModuleVersion, ModuleFileReader::MODULE_FILE_PATH => $sModuleFilePath, 'non_matching_paths' => $aNonMatchingPaths]);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // End of class
|
||||
|
||||
/** Alias for backward compatibility with old module files in which
|
||||
|
||||
190
setup/moduleinstallation/InstallationChoicesService.php
Normal file
190
setup/moduleinstallation/InstallationChoicesService.php
Normal file
@@ -0,0 +1,190 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @copyright Copyright (C) 2010-2025 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
use Combodo\iTop\Setup\ModuleDependency\DependencyExpression;
|
||||
|
||||
require_once __DIR__.'/ModuleInstallationException.php';
|
||||
require_once(APPROOT.'/setup/moduledependency/module.class.inc.php');
|
||||
|
||||
class InstallationChoicesService
|
||||
{
|
||||
private static ?InstallationChoicesService $oInstance;
|
||||
|
||||
protected function __construct()
|
||||
{
|
||||
}
|
||||
|
||||
final public static function GetInstance(): InstallationChoicesService
|
||||
{
|
||||
if (!isset(self::$oInstance)) {
|
||||
self::$oInstance = new InstallationChoicesService();
|
||||
}
|
||||
|
||||
return self::$oInstance;
|
||||
}
|
||||
|
||||
final public static function SetInstance(?InstallationChoicesService $oInstance): void
|
||||
{
|
||||
self::$oInstance = $oInstance;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $aInstallationChoices
|
||||
* @param array $aSearchDirs
|
||||
*
|
||||
* @return array
|
||||
* @throws \ModuleInstallationException
|
||||
*/
|
||||
public function GetInstalledModules(array $aInstallationChoices, array $aSearchDirs, ?string $sInstallationFilePath = null): array
|
||||
{
|
||||
$aPackageModules = ModuleDiscovery::GetAllModules($aSearchDirs);
|
||||
|
||||
$bInstallationFileProvided = ! is_null($sInstallationFilePath) && is_file($sInstallationFilePath);
|
||||
|
||||
if ($bInstallationFileProvided) {
|
||||
$oXMLParameters = new XMLParameters($sInstallationFilePath);
|
||||
$aSteps = $oXMLParameters->Get('steps', []);
|
||||
if (!is_array($aSteps)) {
|
||||
return [];
|
||||
}
|
||||
} else {
|
||||
$aSteps = $aInstallationChoices;
|
||||
}
|
||||
|
||||
$aInstalledModuleNames = $this->FindInstalledPackageModules($aPackageModules, $aInstallationChoices, $aSteps, $bInstallationFileProvided);
|
||||
|
||||
$aInstalledModules = [];
|
||||
foreach (array_keys($aPackageModules) as $sModuleId) {
|
||||
list($sModuleName) = ModuleDiscovery::GetModuleName($sModuleId);
|
||||
if (in_array($sModuleName, $aInstalledModuleNames)) {
|
||||
$aInstalledModules[] = $sModuleId;
|
||||
}
|
||||
}
|
||||
|
||||
return $aInstalledModules;
|
||||
}
|
||||
|
||||
private function FindInstalledPackageModules(array $aPackageModules, array $aInstallationChoices, array $aInstallationDescription, bool $bInstallationFileProvided): array
|
||||
{
|
||||
$aInstalledModules = [];
|
||||
|
||||
$this->ProcessDefaultModules($aPackageModules, $aInstalledModules);
|
||||
|
||||
if ($bInstallationFileProvided) {
|
||||
$this->ProcessInstallationChoices(array_keys($aInstallationChoices), $aInstallationDescription, $aInstalledModules);
|
||||
} else {
|
||||
$aInstalledModules = array_merge($aInstalledModules, $aInstallationChoices);
|
||||
}
|
||||
|
||||
$this->ProcessAutoSelectModules($aPackageModules, $aInstalledModules);
|
||||
|
||||
return array_keys($aInstalledModules);
|
||||
}
|
||||
|
||||
private function ProcessDefaultModules(array $aPackageModules, array &$aInstalledModules): void
|
||||
{
|
||||
foreach ($aPackageModules as $sModuleId => $aModule) {
|
||||
if (($sModuleId != ROOT_MODULE)) {
|
||||
if (isset($aModule['auto_select'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (($aModule['category'] == 'authentication') || (!$aModule['visible'])) {
|
||||
list($sModuleName) = ModuleDiscovery::GetModuleName($sModuleId);
|
||||
$aInstalledModules[$sModuleName] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function ProcessAutoSelectModules(array $aPackageModules, array &$aInstalledModules): void
|
||||
{
|
||||
foreach ($aPackageModules as $sModuleId => $aModule) {
|
||||
if (($sModuleId !== ROOT_MODULE) && isset($aModule['auto_select'])) {
|
||||
try {
|
||||
SetupInfo::SetSelectedModules($aInstalledModules);
|
||||
$bSelected = DependencyExpression::GetPhpExpressionEvaluator()->ParseAndEvaluateBooleanExpression($aModule['auto_select']);
|
||||
if ($bSelected) {
|
||||
list($sModuleName) = ModuleDiscovery::GetModuleName($sModuleId);
|
||||
$aInstalledModules[$sModuleName] = true;
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
IssueLog::Error('Error evaluating module auto-select', null, [
|
||||
'module' => $sModuleId,
|
||||
'error' => $e->getMessage(),
|
||||
'evaluated code' => $aModule['auto_select'],
|
||||
'stacktrace' => $e->getTraceAsString(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function ProcessInstallationChoices(array $aInstallationChoices, array $aInstallationDescription, array &$aInstalledModules): void
|
||||
{
|
||||
foreach ($aInstallationDescription as $aStepInfo) {
|
||||
$aOptions = $aStepInfo['options'] ?? null;
|
||||
if (is_array($aOptions)) {
|
||||
foreach ($aOptions as $aChoiceInfo) {
|
||||
$this->ProcessSelectedChoice($aInstallationChoices, $aChoiceInfo, $aInstalledModules);
|
||||
}
|
||||
}
|
||||
$aOptions = $aStepInfo['alternatives'] ?? null;
|
||||
if (is_array($aOptions)) {
|
||||
foreach ($aOptions as $aChoiceInfo) {
|
||||
$this->ProcessSelectedChoice($aInstallationChoices, $aChoiceInfo, $aInstalledModules);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function ProcessSelectedChoice(array $aInstallationChoices, array $aChoiceInfo, array &$aInstalledModules)
|
||||
{
|
||||
if (!is_array($aChoiceInfo)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$sMandatory = $aChoiceInfo['mandatory'] ?? 'false';
|
||||
|
||||
$aCurrentModules = $aChoiceInfo['modules'] ?? [];
|
||||
$sExtensionCode = $aChoiceInfo['extension_code'];
|
||||
|
||||
$bSelected = ($sMandatory === 'true') || in_array($sExtensionCode, $aInstallationChoices);
|
||||
|
||||
if (!$bSelected) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($aCurrentModules as $sModuleId) {
|
||||
$aInstalledModules[$sModuleId] = true;
|
||||
}
|
||||
|
||||
$aAlternatives = $aChoiceInfo['alternatives'] ?? null;
|
||||
if (is_array($aAlternatives)) {
|
||||
foreach ($aAlternatives as $aSubChoiceInfo) {
|
||||
$this->ProcessSelectedChoice($aInstallationChoices, $aSubChoiceInfo, $aInstalledModules);
|
||||
}
|
||||
}
|
||||
|
||||
$aSubOptionsChoiceInfo = $aChoiceInfo['sub_options'] ?? null;
|
||||
if (is_array($aSubOptionsChoiceInfo)) {
|
||||
$aSubOptions = $aSubOptionsChoiceInfo['options'] ?? null;
|
||||
if (is_array($aSubOptions)) {
|
||||
foreach ($aSubOptions as $aSubChoiceInfo) {
|
||||
$this->ProcessSelectedChoice($aInstallationChoices, $aSubChoiceInfo, $aInstalledModules);
|
||||
}
|
||||
}
|
||||
$aSubAlternatives = $aSubOptionsChoiceInfo['alternatives'] ?? null;
|
||||
if (is_array($aSubAlternatives)) {
|
||||
foreach ($aSubAlternatives as $aSubChoiceInfo) {
|
||||
$this->ProcessSelectedChoice($aInstallationChoices, $aSubChoiceInfo, $aInstalledModules);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
5
setup/moduleinstallation/ModuleInstallationException.php
Normal file
5
setup/moduleinstallation/ModuleInstallationException.php
Normal file
@@ -0,0 +1,5 @@
|
||||
<?php
|
||||
|
||||
class ModuleInstallationException extends Exception
|
||||
{
|
||||
}
|
||||
@@ -32,7 +32,7 @@ require_once APPROOT."setup/modulediscovery.class.inc.php";
|
||||
require_once APPROOT.'setup/modelfactory.class.inc.php';
|
||||
require_once APPROOT.'setup/compiler.class.inc.php';
|
||||
require_once APPROOT.'setup/extensionsmap.class.inc.php';
|
||||
require_once APPROOT.'setup/AnalyzeInstallation.php';
|
||||
require_once APPROOT.'setup/moduleinstallation/AnalyzeInstallation.php';
|
||||
|
||||
define('MODULE_ACTION_OPTIONAL', 1);
|
||||
define('MODULE_ACTION_MANDATORY', 2);
|
||||
@@ -129,7 +129,7 @@ class RunTimeEnvironment
|
||||
*/
|
||||
public function InitDataModel($oConfig, $bModelOnly = true, $bUseCache = false): void
|
||||
{
|
||||
require_once APPROOT.'/setup/moduleinstallation.class.inc.php';
|
||||
require_once APPROOT.'/setup/moduleinstallation/moduleinstallation.class.inc.php';
|
||||
|
||||
$sConfigFile = $oConfig->GetLoadedFile();
|
||||
if (strlen($sConfigFile) > 0) {
|
||||
|
||||
@@ -11,8 +11,6 @@ class AnalyzeInstallationTest extends ItopTestCase
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
$this->RequireOnceItopFile('setup/AnalyzeInstallation.php');
|
||||
$this->RequireOnceItopFile('setup/ModuleInstallationRepository.php');
|
||||
$this->RequireOnceItopFile('setup/modulediscovery.class.inc.php');
|
||||
$this->RequireOnceItopFile('setup/runtimeenv.class.inc.php');
|
||||
}
|
||||
@@ -0,0 +1,211 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @copyright Copyright (C) 2010-2025 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
use Combodo\iTop\Test\UnitTest\ItopDataTestCase;
|
||||
|
||||
class InstallationChoicesServiceTest extends ItopDataTestCase
|
||||
{
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->RequireOnceItopFile('/setup/moduleinstallation/InstallationChoicesService.php');
|
||||
}
|
||||
|
||||
protected function tearDown(): void
|
||||
{
|
||||
parent::tearDown();
|
||||
ModuleDiscovery::ResetCache();
|
||||
}
|
||||
|
||||
public function NonItilExtensionProvider()
|
||||
{
|
||||
return [
|
||||
'all NON ITIL extensions + INCLUDING known-error-mgt' => [
|
||||
'aInstallationChoices' => $this->GetSelectedNonItilExtensions(true, true),
|
||||
'bKnownMgtSelected' => true,
|
||||
'bCoreMgtSelected' => true,
|
||||
],
|
||||
/*'all NON ITIL extensions WITHOUT known-error-mgt' => [
|
||||
'aInstallationChoices' => $this->GetSelectedNonItilExtensions(true, false),
|
||||
'bKnownMgtSelected' => false,
|
||||
'bCoreMgtSelected' => true,
|
||||
],
|
||||
'all NON ITIL extensions WITHOUT core mandatory ones + INCLUDING known-error-mgt' => [
|
||||
'aInstallationChoices' => $this->GetSelectedNonItilExtensions(false, true),
|
||||
'bKnownMgtSelected' => true,
|
||||
'bCoreMgtSelected' => false,
|
||||
],
|
||||
'all NON ITIL extensions WITHOUT core mandatory ones and WITHOUT known-error-mgt' => [
|
||||
'aInstallationChoices' => $this->GetSelectedNonItilExtensions(false, false),
|
||||
'bKnownMgtSelected' => false,
|
||||
'bCoreMgtSelected' => false,
|
||||
],*/
|
||||
];
|
||||
}
|
||||
|
||||
private function GetSelectedNonItilExtensions(bool $coreExtensionIncluded, bool $bKnownMgtIncluded): array
|
||||
{
|
||||
$aExtensions = [
|
||||
'itop-config-mgmt-core',
|
||||
'itop-config-mgmt-datacenter',
|
||||
'itop-config-mgmt-end-user',
|
||||
'itop-config-mgmt-storage',
|
||||
'itop-config-mgmt-virtualization',
|
||||
'itop-service-mgmt-enterprise',
|
||||
'itop-ticket-mgmt-simple-ticket',
|
||||
'itop-ticket-mgmt-simple-ticket-enhanced-portal',
|
||||
'itop-change-mgmt-simple',
|
||||
];
|
||||
|
||||
if ($coreExtensionIncluded) {
|
||||
$aExtensions[] = 'itop-config-mgmt-core';
|
||||
}
|
||||
|
||||
if ($bKnownMgtIncluded) {
|
||||
$aExtensions[] = 'itop-kown-error-mgmt';
|
||||
}
|
||||
|
||||
return $this->TransformExtensionArrayToDictionnary($aExtensions);
|
||||
}
|
||||
|
||||
private function TransformExtensionArrayToDictionnary(array $aExtensions)
|
||||
{
|
||||
$aDict = [];
|
||||
foreach ($aExtensions as $sExtId) {
|
||||
$aDict[$sExtId] = [];
|
||||
}
|
||||
|
||||
return $aDict;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider NonItilExtensionProvider
|
||||
*/
|
||||
public function testGetInstalledModules(array $aInstallationChoices, bool $bKnownMgtSelected, bool $bCoreMgtSelected)
|
||||
{
|
||||
$aSearchDirs = $this->GivenModuleDiscoveryInit();
|
||||
|
||||
$aExpectedModulesIds = $this->GetExpectedNonItilInstalledModules($bKnownMgtSelected, $bCoreMgtSelected);
|
||||
|
||||
$aInstalledModules = InstallationChoicesService::GetInstance()->GetInstalledModules(
|
||||
$aInstallationChoices,
|
||||
$aSearchDirs,
|
||||
__DIR__.'/ressources/installation.xml'
|
||||
);
|
||||
|
||||
sort($aInstalledModules);
|
||||
$this->assertEquals($aExpectedModulesIds, $aInstalledModules);
|
||||
}
|
||||
|
||||
public function testGetInstalledModules_NoInstallationXML()
|
||||
{
|
||||
$aSearchDirs = $this->GivenModuleDiscoveryInit();
|
||||
|
||||
$aInstallationChoices = json_decode(file_get_contents(__DIR__."/ressources/installation_choices_when_no_xml_file.json"), true);
|
||||
|
||||
$aExpectedModulesIds = [
|
||||
'authent-cas/3.3.0',
|
||||
'authent-external/3.3.0',
|
||||
'authent-ldap/3.3.0',
|
||||
'authent-local/3.3.0',
|
||||
'combodo-backoffice-darkmoon-theme/3.3.0',
|
||||
'combodo-backoffice-fullmoon-high-contrast-theme/3.3.0',
|
||||
'combodo-backoffice-fullmoon-protanopia-deuteranopia-theme/3.3.0',
|
||||
'combodo-backoffice-fullmoon-tritanopia-theme/3.3.0',
|
||||
'combodo-db-tools/3.3.0',
|
||||
'itop-attachments/3.3.0',
|
||||
'itop-backup/3.3.0',
|
||||
'itop-bridge-cmdb-services/3.3.0',
|
||||
'itop-config-mgmt/3.3.0',
|
||||
'itop-config/3.3.0',
|
||||
'itop-core-update/3.3.0',
|
||||
'itop-faq-light/3.3.0',
|
||||
'itop-files-information/3.3.0',
|
||||
'itop-portal-base/3.3.0',
|
||||
'itop-profiles-itil/3.3.0',
|
||||
'itop-request-mgmt/3.3.0',
|
||||
'itop-service-mgmt/3.3.0',
|
||||
'itop-sla-computation/3.3.0',
|
||||
'itop-structure/3.3.0',
|
||||
'itop-welcome-itil/3.3.0',
|
||||
];
|
||||
|
||||
$aInstalledModules = InstallationChoicesService::GetInstance()->GetInstalledModules(
|
||||
$aInstallationChoices,
|
||||
$aSearchDirs,
|
||||
null
|
||||
);
|
||||
|
||||
sort($aInstalledModules);
|
||||
$this->assertEquals($aExpectedModulesIds, $aInstalledModules);
|
||||
}
|
||||
|
||||
private function GetExpectedNonItilInstalledModules(bool $bKnownMgtSelected, bool $bCoreMgtSelected): array
|
||||
{
|
||||
$aExpectedInstalledModules = [
|
||||
'itop-structure/3.3.0',
|
||||
'itop-sla-computation/3.3.0',
|
||||
'itop-portal-base/3.3.0',
|
||||
'itop-bridge-virtualization-mgmt-services/3.3.0',
|
||||
'itop-request-mgmt/3.3.0',
|
||||
'itop-welcome-itil/3.3.0',
|
||||
'itop-profiles-itil/3.3.0',
|
||||
'authent-ldap/3.3.0',
|
||||
'authent-local/3.3.0',
|
||||
'itop-oauth-client/3.3.0',
|
||||
'itop-virtualization-mgmt/3.3.0',
|
||||
'itop-core-update/3.3.0',
|
||||
'combodo-backoffice-fullmoon-high-contrast-theme/3.3.0',
|
||||
'itop-bridge-cmdb-services/3.3.0',
|
||||
'itop-files-information/3.3.0',
|
||||
'itop-attachments/3.3.0',
|
||||
'itop-bridge-endusers-devices-services/3.3.0',
|
||||
'itop-endusers-devices/3.3.0',
|
||||
'itop-backup/3.3.0',
|
||||
'itop-config/3.3.0',
|
||||
'itop-bridge-storage-mgmt-services/3.3.0',
|
||||
'combodo-backoffice-fullmoon-tritanopia-theme/3.3.0',
|
||||
'combodo-backoffice-darkmoon-theme/3.3.0',
|
||||
'itop-portal/3.3.0',
|
||||
'itop-hub-connector/3.3.0',
|
||||
'itop-bridge-datacenter-mgmt-services/3.3.0',
|
||||
'itop-themes-compat/3.3.0',
|
||||
'itop-tickets/3.3.0',
|
||||
'itop-storage-mgmt/3.3.0',
|
||||
'combodo-db-tools/3.3.0',
|
||||
'combodo-backoffice-fullmoon-protanopia-deuteranopia-theme/3.3.0',
|
||||
'authent-cas/3.3.0',
|
||||
'itop-datacenter-mgmt/3.3.0',
|
||||
'itop-bridge-virtualization-storage/3.3.0',
|
||||
'authent-external/3.3.0',
|
||||
'itop-bridge-cmdb-ticket/3.3.0',
|
||||
|
||||
'itop-change-mgmt/3.3.0',
|
||||
'itop-service-mgmt/3.3.0',
|
||||
];
|
||||
if ($bCoreMgtSelected) {
|
||||
$aExpectedInstalledModules [] = 'itop-config-mgmt/3.3.0';
|
||||
}
|
||||
if ($bKnownMgtSelected) {
|
||||
$aExpectedInstalledModules [] = 'itop-knownerror-mgmt/3.3.0';
|
||||
$aExpectedInstalledModules [] = 'itop-faq-light/3.3.0';
|
||||
}
|
||||
sort($aExpectedInstalledModules);
|
||||
|
||||
return $aExpectedInstalledModules;
|
||||
}
|
||||
|
||||
private function GivenModuleDiscoveryInit(): array
|
||||
{
|
||||
$aSearchDirs = [APPROOT.'datamodels/2.x'];
|
||||
$this->SetNonPublicStaticProperty(ModuleDiscovery::class, 'm_aSearchDirs', $aSearchDirs);
|
||||
$aAllModules = json_decode(file_get_contents(__DIR__.'/ressources/available_modules.json'), true);
|
||||
$this->SetNonPublicStaticProperty(ModuleDiscovery::class, 'm_aModules', $aAllModules);
|
||||
return $aSearchDirs;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,230 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<installation>
|
||||
<steps type="array">
|
||||
<step>
|
||||
<title>Configuration Management options</title>
|
||||
<description><![CDATA[<h2>The options below allow you to configure the type of elements that are to be managed inside iTop.</h2>]]></description>
|
||||
<banner>/images/icons/icons8-apps-tab.svg</banner>
|
||||
<options type="array">
|
||||
<choice>
|
||||
<extension_code>itop-config-mgmt-core</extension_code>
|
||||
<title>Configuration Management Core</title>
|
||||
<description>All the base objects that are mandatory in the iTop CMDB: Organizations, Locations, Teams, Persons, etc.</description>
|
||||
<modules type="array">
|
||||
<module>combodo-backoffice-darkmoon-theme</module>
|
||||
<module>combodo-backoffice-fullmoon-high-contrast-theme</module>
|
||||
<module>combodo-backoffice-fullmoon-protanopia-deuteranopia-theme</module>
|
||||
<module>combodo-backoffice-fullmoon-tritanopia-theme</module>
|
||||
<module>combodo-db-tools</module>
|
||||
<module>combodo-password-expiration</module>
|
||||
<module>combodo-webhook-integration</module>
|
||||
<module>itop-attachments</module>
|
||||
<module>itop-backup</module>
|
||||
<module>itop-config</module>
|
||||
<module>itop-config-mgmt</module>
|
||||
<module>itop-core-update</module>
|
||||
<module>itop-files-information</module>
|
||||
<module>itop-hub-connector</module>
|
||||
<module>itop-oauth-client</module>
|
||||
<module>itop-profiles-itil</module>
|
||||
<module>itop-structure</module>
|
||||
<module>itop-themes-compat</module>
|
||||
<module>itop-tickets</module>
|
||||
<module>itop-welcome-itil</module>
|
||||
<module>combodo-my-account-user-info</module>
|
||||
<module>authent-token</module>
|
||||
</modules>
|
||||
<mandatory>true</mandatory>
|
||||
</choice>
|
||||
<choice>
|
||||
<extension_code>itop-config-mgmt-datacenter</extension_code>
|
||||
<title>Data Center Devices</title>
|
||||
<description>Manage Data Center devices such as Racks, Enclosures, PDUs, etc.</description>
|
||||
<modules type="array">
|
||||
<module>itop-datacenter-mgmt</module>
|
||||
</modules>
|
||||
<default>true</default>
|
||||
</choice>
|
||||
<choice>
|
||||
<extension_code>itop-config-mgmt-end-user</extension_code>
|
||||
<title>End-User Devices</title>
|
||||
<description>Manage devices related to end-users: PCs, Phones, Tablets, etc.</description>
|
||||
<modules type="array">
|
||||
<module>itop-endusers-devices</module>
|
||||
</modules>
|
||||
<default>true</default>
|
||||
</choice>
|
||||
<choice>
|
||||
<extension_code>itop-config-mgmt-storage</extension_code>
|
||||
<title>Storage Devices</title>
|
||||
<description>Manage storage devices such as NAS, SAN Switches, Tape Libraries and Tapes, etc.</description>
|
||||
<modules type="array">
|
||||
<module>itop-storage-mgmt</module>
|
||||
</modules>
|
||||
<default>true</default>
|
||||
</choice>
|
||||
<choice>
|
||||
<extension_code>itop-config-mgmt-virtualization</extension_code>
|
||||
<title>Virtualization</title>
|
||||
<description>Manage Hypervisors, Virtual Machines and Farms.</description>
|
||||
<modules type="array">
|
||||
<module>itop-virtualization-mgmt</module>
|
||||
</modules>
|
||||
<default>true</default>
|
||||
</choice>
|
||||
</options>
|
||||
</step>
|
||||
<step>
|
||||
<title>Service Management options</title>
|
||||
<description><![CDATA[<h2>Select the choice that best describes the relationships between the services and the IT infrastructure in your IT environment.</h2>]]></description>
|
||||
<banner>/images/icons/icons8-services.svg</banner>
|
||||
<alternatives type="array">
|
||||
<choice>
|
||||
<extension_code>itop-service-mgmt-enterprise</extension_code>
|
||||
<title>Service Management for Enterprises</title>
|
||||
<description>Select this option if the IT delivers services based on a shared infrastructure. For example if different organizations within your company subscribe to services (like Mail and Print services) delivered by a single shared backend.</description>
|
||||
<modules type="array">
|
||||
<module>itop-service-mgmt</module>
|
||||
</modules>
|
||||
<default>true</default>
|
||||
</choice>
|
||||
<choice>
|
||||
<extension_code>itop-service-mgmt-service-provider</extension_code>
|
||||
<title>Service Management for Service Providers</title>
|
||||
<description>Select this option if the IT manages the infrastructure of independent customers. This is the most flexible model, since the services can be delivered with a mix of shared and customer specific infrastructure devices.</description>
|
||||
<modules type="array">
|
||||
<module>itop-service-mgmt-provider</module>
|
||||
</modules>
|
||||
</choice>
|
||||
</alternatives>
|
||||
</step>
|
||||
<step>
|
||||
<title>Tickets Management options</title>
|
||||
<description><![CDATA[<h2>Select the type of tickets you want to use in order to respond to user requests and incidents.</h2>]]></description>
|
||||
<banner>/images/icons/icons8-discussion-forum.svg</banner>
|
||||
<alternatives type="array">
|
||||
<choice>
|
||||
<extension_code>itop-ticket-mgmt-simple-ticket</extension_code>
|
||||
<title>Simple Ticket Management</title>
|
||||
<description>Select this option to use one single type of tickets for all kind of requests.</description>
|
||||
<modules type="array">
|
||||
<module>itop-request-mgmt</module>
|
||||
</modules>
|
||||
<default>true</default>
|
||||
<sub_options>
|
||||
<options type="array">
|
||||
<choice>
|
||||
<extension_code>itop-ticket-mgmt-simple-ticket-enhanced-portal</extension_code>
|
||||
<title>Customer Portal</title>
|
||||
<description><![CDATA[Modern & responsive portal for the end-users]]></description>
|
||||
<modules type="array">
|
||||
<module>itop-portal</module>
|
||||
<module>itop-portal-base</module>
|
||||
</modules>
|
||||
<default>true</default>
|
||||
</choice>
|
||||
</options>
|
||||
</sub_options>
|
||||
</choice>
|
||||
<choice>
|
||||
<extension_code>itop-ticket-mgmt-itil</extension_code>
|
||||
<title>ITIL Compliant Tickets Management</title>
|
||||
<description>Select this option to have different types of ticket for managing user requests and incidents. Each type of ticket has a specific life cycle and specific fields</description>
|
||||
<sub_options>
|
||||
<options type="array">
|
||||
<choice>
|
||||
<extension_code>itop-ticket-mgmt-itil-user-request</extension_code>
|
||||
<title>User Request Management</title>
|
||||
<description>Manage User Request tickets in iTop</description>
|
||||
<modules type="array">
|
||||
<module>itop-request-mgmt-itil</module>
|
||||
</modules>
|
||||
</choice>
|
||||
<choice>
|
||||
<extension_code>itop-ticket-mgmt-itil-incident</extension_code>
|
||||
<title>Incident Management</title>
|
||||
<description>Manage Incidents tickets in iTop</description>
|
||||
<modules type="array">
|
||||
<module>itop-incident-mgmt-itil</module>
|
||||
</modules>
|
||||
</choice>
|
||||
<choice>
|
||||
<extension_code>itop-ticket-mgmt-itil-enhanced-portal</extension_code>
|
||||
<title>Customer Portal</title>
|
||||
<description><![CDATA[Modern & responsive portal for the end-users]]></description>
|
||||
<modules type="array">
|
||||
<module>itop-portal</module>
|
||||
<module>itop-portal-base</module>
|
||||
</modules>
|
||||
<default>true</default>
|
||||
</choice>
|
||||
</options>
|
||||
</sub_options>
|
||||
</choice>
|
||||
<choice>
|
||||
<extension_code>itop-ticket-mgmt-none</extension_code>
|
||||
<title>No Tickets Management</title>
|
||||
<description>Don't manage incidents or user requests in iTop</description>
|
||||
<modules type="array">
|
||||
</modules>
|
||||
</choice>
|
||||
</alternatives>
|
||||
</step>
|
||||
<step>
|
||||
<title>Change Management options</title>
|
||||
<description><![CDATA[<h2>Select the type of tickets you want to use in order to manage changes to the IT infrastructure.</h2>]]></description>
|
||||
<banner>/images/icons/icons8-change.svg</banner>
|
||||
<alternatives type="array">
|
||||
<choice>
|
||||
<extension_code>itop-change-mgmt-simple</extension_code>
|
||||
<title>Simple Change Management</title>
|
||||
<description>Select this option to use one type of ticket for all kind of changes.</description>
|
||||
<modules type="array">
|
||||
<module>itop-change-mgmt</module>
|
||||
</modules>
|
||||
<default>true</default>
|
||||
</choice>
|
||||
<choice>
|
||||
<extension_code>itop-change-mgmt-itil</extension_code>
|
||||
<title>ITIL Change Management</title>
|
||||
<description>Select this option to use Normal/Routine/Emergency change tickets.</description>
|
||||
<modules type="array">
|
||||
<module>itop-change-mgmt-itil</module>
|
||||
</modules>
|
||||
</choice>
|
||||
<choice>
|
||||
<extension_code>itop-change-mgmt-none</extension_code>
|
||||
<title>No Change Management</title>
|
||||
<description>Don't manage changes in iTop</description>
|
||||
<modules type="array">
|
||||
</modules>
|
||||
</choice>
|
||||
</alternatives>
|
||||
</step>
|
||||
<step>
|
||||
<title>Additional ITIL tickets</title>
|
||||
<description><![CDATA[<h2>Pick from the list below the additional ITIL processes that are to be implemented in iTop.</h2>]]></description>
|
||||
<banner>/images/icons/icons8-important-book.svg</banner>
|
||||
<options type="array">
|
||||
<choice>
|
||||
<!-- Extension code has a typo but fixing it would remove that setup option for all existing iTop -->
|
||||
<extension_code>itop-kown-error-mgmt</extension_code>
|
||||
<title>Known Errors Management and FAQ</title>
|
||||
<description>Select this option to track "Known Errors" and FAQs in iTop.</description>
|
||||
<modules type="array">
|
||||
<module>itop-faq-light</module>
|
||||
<module>itop-knownerror-mgmt</module>
|
||||
</modules>
|
||||
</choice>
|
||||
<choice>
|
||||
<extension_code>itop-problem-mgmt</extension_code>
|
||||
<title>Problem Management</title>
|
||||
<description>Select this option track "Problems" in iTop.</description>
|
||||
<modules type="array">
|
||||
<module>itop-problem-mgmt</module>
|
||||
</modules>
|
||||
</choice>
|
||||
</options>
|
||||
</step>
|
||||
</steps>
|
||||
</installation>
|
||||
@@ -0,0 +1 @@
|
||||
{"itop-config-mgmt":{"label":"Configuration+Management+customized+for+Combodo+IT(CMDB)","value":"2.7.0"},"itop-icalendar-action":{"label":"Calendar+Invitations","value":"1.1.0"},"itop-fence":{"label":"iTop+Fence","value":"1.1.2"},"authent-ldap":{"label":"User+authentication+based+on+LDAP","value":"3.2.1"},"itop-faq-light":{"label":"Frequently+Asked+Questions+Database","value":"3.2.1"},"authent-local":{"label":"User+authentication+based+on+the+local+DB","value":"3.2.1"},"combodo-custom-hyperlinks":{"label":"Hyperlinks+configurator","value":"1.1.3"},"authent-token":{"label":"User+authentication+by+token","value":"2.2.1"},"itop-service-mgmt":{"label":"Service+Management+Customized+for+Combodo+IT(services,+SLAs,+contracts)","value":"2.7.0"},"combodo-impersonate":{"label":"Impersonate+user+for+support","value":"1.2.1"},"combodo-hybridauth":{"label":"oAuth\/OpenID+authentication","value":"1.2.4"},"combodo-login-page":{"label":"Combodo+login+page","value":"2.1.0"},"itop-core-update":{"label":"iTop+Core+Update","value":"3.2.1"},"itop-communications":{"label":"Communications+to+the+Customers","value":"1.3.4"},"itsm-designer-connector":{"label":"ITSM+Designer+Connector","value":"1.8.3"},"authent-external":{"label":"External+user+authentication","value":"3.2.1"},"itop-object-copier":{"label":"Object+copier","value":"1.4.5"},"combodo-backoffice-compact-themes":{"label":"Backoffice:+compact+themes","value":"1.0.1"},"data-localizer":{"label":"Data+localizer","value":"1.3.4"},"combodo-support-portal":{"label":"Combodo+Support+Portal","value":"3.0.1"},"combodo-calendar-view":{"label":"Calendar+View","value":"2.2.1"},"combodo-email-synchro":{"label":"Tickets+synchronization+via+e-mail","value":"3.8.2"},"combodo-webhook-integration":{"label":"Webhook+integrations","value":"1.4.1"},"combodo-notify-on-expiration":{"label":"Notify+on+expiration","value":"1.0.4"},"combodo-db-tools":{"label":"Database+maintenance+tools","value":"3.2.1"},"precanned-replies":{"label":"Helpdesk+Precanned+Replies","value":"1.4.0"},"combodo-dokuwiki-portal-brick":{"label":"Docuwiki+brick+(Portal)","value":"1.2.0"},"itop-rh-mgmt":{"label":"Human+Resource+Management","value":"2.7.0"},"itop-request-mgmt":{"label":"User+request+management+(Service+Desk)","value":"2.7.0"},"customer-survey":{"label":"Customer+Survey","value":"2.5.5"},"itop-standard-email-synchro":{"label":"Ticket+Creation+from+Emails+(Standard)","value":"3.8.2"},"itop-system-information":{"label":"System+information","value":"1.2.6"},"itop-sales-mgmt":{"label":"Sales+Management","value":"2.7.0"},"combodo-password-expiration":{"label":"Password+Expiration+Enforcement","value":"1.0.0"},"combodo-workflow-graphical-view":{"label":"Workflow+graphical+view","value":"1.1.3"},"combodo-itsm-master":{"label":"Data+master+for+the+ITSM+Designer","value":"2.7.0"},"combodo-email-tickets":{"label":"Tickets+Creation+from+Emails+for+Combodo","value":"2.7.0"},"itop-training-mgmt":{"label":"Training+Management","value":"2.7.0"},"precanned-replies-pro":{"label":"Helpdesk+Precanned+Replies+Extension","value":"1.2.0"},"combodo-fulltext-search":{"label":"Enhanced+global+search","value":"2.0.0"},"itop-request-template":{"label":"Customized+Request+Forms","value":"2.3.6"},"itop-rest-data-push":{"label":"Data+push+(based+on+standard+REST+services)","value":"1.0.2"},"combodo-kpi-logger":{"label":"KPI+logger","value":"1.0.3"},"itop-incident-mgmt":{"label":"Incident+Management","value":"2.7.0"},"combodo-my-account-user-info":{"label":"User+info+for+MyAccount+module","value":"1.0.0"},"email-reply":{"label":"Send+Ticket+Log+Updates+by+Email","value":"1.4.5"},"itop-attachments":{"label":"Tickets+Attachments","value":"3.2.1"},"itop-log-mgmt":{"label":"iTop+Log+management","value":"2.0.8"},"itop-ui-copypaste":{"label":"CopyPaste+UI+Component","value":"1.0.0"}}
|
||||
Reference in New Issue
Block a user