N°9567 - Extension Mgmt : Run setup

This commit is contained in:
odain
2026-05-19 10:46:56 +02:00
parent 908e11d507
commit 1ab035ffaa
6 changed files with 349 additions and 74 deletions

View File

@@ -38,7 +38,7 @@ require_once(APPROOT.'setup/extensionsmap.class.inc.php');
class WizardController class WizardController
{ {
protected $aSteps; protected $aWizardSteps;
protected $sInitialStepClass; protected $sInitialStepClass;
protected $sInitialState; protected $sInitialState;
protected $aParameters; protected $aParameters;
@@ -53,7 +53,7 @@ class WizardController
$this->sInitialStepClass = $sInitialStepClass; $this->sInitialStepClass = $sInitialStepClass;
$this->sInitialState = $sInitialState; $this->sInitialState = $sInitialState;
$this->aParameters = []; $this->aParameters = [];
$this->aSteps = []; $this->aWizardSteps = [];
} }
/** /**
@@ -62,7 +62,7 @@ class WizardController
*/ */
protected function PushStep($aStepInfo) protected function PushStep($aStepInfo)
{ {
array_push($this->aSteps, $aStepInfo); array_push($this->aWizardSteps, $aStepInfo);
} }
/** /**
@@ -71,7 +71,7 @@ class WizardController
*/ */
protected function PopStep() protected function PopStep()
{ {
return array_pop($this->aSteps); return array_pop($this->aWizardSteps);
} }
/** /**
@@ -235,9 +235,9 @@ HTML;
$oPage->add('<input type="hidden" name="_params['.$sCode.']" value="'.utils::EscapeHtml($value).'"/>'); $oPage->add('<input type="hidden" name="_params['.$sCode.']" value="'.utils::EscapeHtml($value).'"/>');
} }
$oPage->add('<input type="hidden" name="_steps" value="'.utils::EscapeHtml(json_encode($this->aSteps)).'"/>'); $oPage->add('<input type="hidden" name="_steps" value="'.utils::EscapeHtml(json_encode($this->aWizardSteps)).'"/>');
$oPage->add('<table style="width:100%;" class="ibo-setup--wizard--buttons-container"><tr>'); $oPage->add('<table style="width:100%;" class="ibo-setup--wizard--buttons-container"><tr>');
if ((count($this->aSteps) > 0) && ($oStep->CanMoveBackward())) { if ((count($this->aWizardSteps) > 0) && ($oStep->CanMoveBackward())) {
$oPage->add('<td style="text-align: left"><button id="btn_back" class="ibo-button ibo-is-alternative ibo-is-neutral" type="submit" name="operation" value="back"><span class="ibo-button--label">Back</span></button></td>'); $oPage->add('<td style="text-align: left"><button id="btn_back" class="ibo-button ibo-is-alternative ibo-is-neutral" type="submit" name="operation" value="back"><span class="ibo-button--label">Back</span></button></td>');
} }
if ($oStep->CanMoveForward()) { if ($oStep->CanMoveForward()) {
@@ -296,7 +296,7 @@ on the page's parameters
$sOperation = utils::ReadParam('operation'); $sOperation = utils::ReadParam('operation');
$this->aParameters = utils::ReadParam('_params', [], false, 'raw_data'); $this->aParameters = utils::ReadParam('_params', [], false, 'raw_data');
$this->SetSteps(json_decode(utils::ReadParam('_steps', '[]', false, 'raw_data'), true)); $this->SetWizardSteps(json_decode(utils::ReadParam('_steps', '[]', false, 'raw_data'), true));
switch ($sOperation) { switch ($sOperation) {
case 'next': case 'next':
@@ -371,9 +371,9 @@ on the page's parameters
return $sOutput; return $sOutput;
} }
public function SetSteps(array $aSteps): void public function SetWizardSteps(array $aWizardSteps): void
{ {
$this->aSteps = $aSteps; $this->aWizardSteps = $aWizardSteps;
} }
/** /**

View File

@@ -40,25 +40,6 @@ class WizStepLandingBeforeAudit extends WizStepModulesChoice
*/ */
public function UpdateWizardStateAndGetNextStep($bMoveForward = true): WizardState public function UpdateWizardStateAndGetNextStep($bMoveForward = true): WizardState
{ {
$this->oWizard->SetParameter('selected_components', '[{"_0":"_0","_1":"_1","_2":"_2","_3":"_3","_4":"_4"},{"_0":"_0"},{"_0":"_0","_0_0":"_0_0"},{"_0":"_0"},{"_0":"_0","_1":"_1"},{"_0":"_0","_1":"_1"}]');
$aSteps = json_decode(
'[
{"class":"WizStepWelcome","state":""},
{"class":"WizStepInstallOrUpgrade","state":""},
{"class":"WizStepDetectedInfo","state":""},
{"class":"WizStepUpgradeMiscParams","state":""},
{"class":"WizStepModulesChoice","state":"start_upgrade"},
{"class":"WizStepModulesChoice","state":"1"},
{"class":"WizStepModulesChoice","state":"2"},
{"class":"WizStepModulesChoice","state":"3"}]',
true
);
$this->oWizard->SetSteps($aSteps);
$this->aSteps = $aSteps;
$this->sCurrentState = count($aSteps) - 1;
//parent::UpdateWizardStateAndGetNextStep(true);
$oProductionEnv = new RunTimeEnvironment(); $oProductionEnv = new RunTimeEnvironment();
$sBuildConfigFile = APPCONF.$oProductionEnv->GetBuildEnv().'/'.ITOP_CONFIG_FILE; $sBuildConfigFile = APPCONF.$oProductionEnv->GetBuildEnv().'/'.ITOP_CONFIG_FILE;
@chmod($sBuildConfigFile, 0770); // In case it exists: RWX for owner and group, nothing for others @chmod($sBuildConfigFile, 0770); // In case it exists: RWX for owner and group, nothing for others
@@ -79,6 +60,13 @@ class WizStepLandingBeforeAudit extends WizStepModulesChoice
$this->oWizard->SaveParameter('removed_extensions', []); $this->oWizard->SaveParameter('removed_extensions', []);
$this->oWizard->SaveParameter('extensions_not_uninstallable', []); $this->oWizard->SaveParameter('extensions_not_uninstallable', []);
$aWizardSteps = $this->GetWizardSteps();
$this->oWizard->SetWizardSteps($aWizardSteps);
$this->sCurrentState = count($aWizardSteps) - 1;
$aSelectedComponents = $this->GetSelectedComponents($this->aSteps, $this->oWizard->GetParameter('selected_extensions'));
$this->oWizard->SetParameter('selected_components', json_encode($aSelectedComponents));
return new WizardState(WizStepDataAudit::class); return new WizardState(WizStepDataAudit::class);
} }

View File

@@ -120,46 +120,6 @@ class WizStepModulesChoice extends AbstractWizStepInstall
return [$aExtensionsAdded, $aExtensionsRemoved, $aExtensionsNotUninstallable]; return [$aExtensionsAdded, $aExtensionsRemoved, $aExtensionsNotUninstallable];
} }
public static function GetSetupComponentsFromExtensions(Config $oConfig, array $aSelectedExtensions): array
{
$sSourceFile = APPROOT.'datamodels/2.x/installation.xml';
$oExtensionsMap = new iTopExtensionsMap();
$oExtensionsMap->LoadChoicesFromDatabase($oConfig);
$aOptions = $oExtensionsMap->GetAllExtensionsOptionInfo(false);
if (is_file($sSourceFile)) {
$aParams = new XMLParameters($sSourceFile);
$aSteps = $aParams->Get('steps', []);
// Display this step of the wizard only if there is something to display
if (count($aOptions) > 0) {
$aSteps[] = [
'title' => 'Extensions',
'description' => '<h2>Select additional extensions to install. You can launch the installation again to install new extensions or remove installed ones.</h2>',
'banner' => '/images/icons/icons8-puzzle.svg',
'options' => $aOptions,
];
}
} else {
//legacy package
$aSteps = [
[
'title' => 'Modules Selection',
'description' => '<h2>Select the modules to install. You can launch the installation again to install new modules, but you cannot remove already installed modules.</h2>',
'banner' => '/images/icons/icons8-apps-tab.svg',
'options' => $aOptions,
],
];
}
$aRes = [];
foreach ($aSteps as $aStep) {
}
return [];
}
public function UpdateWizardStateAndGetNextStep($bMoveForward = true): WizardState public function UpdateWizardStateAndGetNextStep($bMoveForward = true): WizardState
{ {
// Accumulates the selected modules: // Accumulates the selected modules:
@@ -206,6 +166,86 @@ class WizStepModulesChoice extends AbstractWizStepInstall
return new WizardState(WizStepModulesChoice::class, (string)($index - 1)); return new WizardState(WizStepModulesChoice::class, (string)($index - 1));
} }
public function GetWizardSteps(): array
{
$aSteps = [
["class" => "WizStepWelcome","state" => ""],
["class" => "WizStepInstallOrUpgrade","state" => ""],
["class" => "WizStepDetectedInfo","state" => ""],
["class" => "WizStepUpgradeMiscParams","state" => ""],
];
$i = 0;
while (null != $this->GetStepInfo($i)) {
$aSteps [] = ["class" => "WizStepModulesChoice","state" => "$i"];
$i++;
}
return $aSteps;
}
public function GetSelectedComponents(array $aSteps, string $sSelectedExtensionJson): array
{
SetupLog::Error(__METHOD__, null, $aSteps);
$aExtensions = json_decode($sSelectedExtensionJson, true);
$aRes = [];
foreach ($aSteps as $i => $aStepInfo) {
$aStepRes = [];
$this->ProcessOptions("", $aStepInfo, $aExtensions, $aStepRes);
$this->ProcessAlternatives("", $aStepInfo, $aExtensions, $aStepRes);
$aRes [] = $aStepRes;
}
return $aRes;
}
public function ProcessOptions(string $sCurrentIndex, array $aInfo, array $aExtensions, array &$aStepRes)
{
$aOptions = $aInfo["options"] ?? null;
if (is_null($aOptions) || !is_array($aOptions)) {
return;
}
foreach ($aOptions as $i => $aOptionsInfo) {
$sExtensionCode = $aOptionsInfo["extension_code"] ?? null;
if (in_array($sExtensionCode, $aExtensions)) {
$sNextIndex = "{$sCurrentIndex}_{$i}";
$aStepRes[$sNextIndex] = $sNextIndex;
$aSubOptions = $aOptionsInfo['sub_options'] ?? null;
if (!is_null($aSubOptions) && is_array($aSubOptions)) {
$this->ProcessOptions($sNextIndex, $aSubOptions, $aExtensions, $aStepRes);
}
$this->ProcessAlternatives($sNextIndex, $aOptionsInfo, $aExtensions, $aStepRes);
}
}
}
public function ProcessAlternatives(string $sCurrentIndex, array $aInfo, array $aExtensions, array &$aStepRes)
{
$aAlternatives = $aInfo["alternatives"] ?? null;
if (is_null($aAlternatives) || ! is_array($aAlternatives)) {
return;
}
foreach ($aAlternatives as $i => $aAlternativeInfo) {
$sExtensionCode = $aAlternativeInfo["extension_code"] ?? null;
if (in_array($sExtensionCode, $aExtensions)) {
$sNextIndex = "{$sCurrentIndex}_{$i}";
$aStepRes [$sNextIndex] = $sNextIndex;
$aSubOptions = $aAlternativeInfo['sub_options'] ?? null;
if (!is_null($aSubOptions) && is_array($aSubOptions)) {
$this->ProcessOptions($sNextIndex, $aSubOptions, $aExtensions, $aStepRes);
}
break;
}
}
}
public function Display(SetupPage $oPage): void public function Display(SetupPage $oPage): void
{ {
$this->DisplayStep($oPage); $this->DisplayStep($oPage);

View File

@@ -159,10 +159,10 @@ class ExtensionsMapTest extends ItopTestCase
$sExpected = file_get_contents(__DIR__.'/ressources/all_extensions_from_datamodels.json'); $sExpected = file_get_contents(__DIR__.'/ressources/all_extensions_from_datamodels.json');
$sExpected = str_replace('"sVersion": "ITOP_VERSION"', '"sVersion": "'.ITOP_VERSION.'"', $sExpected); $sExpected = str_replace('"sVersion": "ITOP_VERSION"', '"sVersion": "'.ITOP_VERSION.'"', $sExpected);
$sExpected = preg_replace('/"module_file_path": .*/', '"module_file_path": ANYPATH', $sExpected); $sExpected = preg_replace('/"module_file_path": .*/', '"module_file_path": ANYPATH', $sExpected);
$actual = json_encode($this->SerializeExtensionMap($oiTopExtensionsMap), JSON_PRETTY_PRINT); $actual = json_encode($this->SerializeExtensionMap($oiTopExtensionsMap), JSON_PRETTY_PRINT);
$actual = preg_replace('/"module_file_path": .*/', '"module_file_path": ANYPATH', $actual); $actual = preg_replace('/"module_file_path": .*/', '"module_file_path": ANYPATH', $actual);
$this->assertEquals($sExpected, $actual); $this->assertEquals($sExpected, $actual);
} }

View File

@@ -1392,11 +1392,15 @@ HTML,
$this->assertEquals($sExpectedHTML, $oPage->sContent); $this->assertEquals($sExpectedHTML, $oPage->sContent);
} }
public function testGetSetupComponentsFromExtensions() public function testGetSelectedComponents()
{ {
$aParams = new XMLParameters(__DIR__.'/ressources/installation_330.xml');
$aSteps = $aParams->Get('steps', []);
$aSelectedExtensions = ["itop-config-mgmt-core","itop-config-mgmt-datacenter","itop-config-mgmt-end-user","itop-config-mgmt-storage","itop-config-mgmt-virtualization","itop-container-mgmt","itop-service-mgmt-enterprise","itop-ticket-mgmt-simple-ticket","itop-ticket-mgmt-simple-ticket-enhanced-portal","itop-change-mgmt-simple","itop-kown-error-mgmt","itop-problem-mgmt","combodo-oauth2-client","combodo-mfa-extended","combodo-data-replication","combodo-api-playground","combodo-snapshot"]; $aSelectedExtensions = ["itop-config-mgmt-core","itop-config-mgmt-datacenter","itop-config-mgmt-end-user","itop-config-mgmt-storage","itop-config-mgmt-virtualization","itop-container-mgmt","itop-service-mgmt-enterprise","itop-ticket-mgmt-simple-ticket","itop-ticket-mgmt-simple-ticket-enhanced-portal","itop-change-mgmt-simple","itop-kown-error-mgmt","itop-problem-mgmt","combodo-oauth2-client","combodo-mfa-extended","combodo-data-replication","combodo-api-playground","combodo-snapshot"];
$aExpected = json_decode('[{"_0":"_0","_1":"_1","_2":"_2","_3":"_3","_4":"_4","_4_0":"_4_0"},{"_0":"_0"},{"_0":"_0","_0_0":"_0_0"},{"_0":"_0"},{"_0":"_0","_1":"_1"},{"_0":"_0","_1":"_1","_3":"_3","_4":"_4","_5":"_5"}]', true); $aRes = $this->oStep->GetSelectedComponents($aSteps, json_encode($aSelectedExtensions));
// $this->assertEquals($aExpected, \WizStepModulesChoice::GetSetupComponentsFromExtensions($aSelectedExtensions));
$this->assertTrue(true); $aExpected = json_decode('[{"_0":"_0","_1":"_1","_2":"_2","_3":"_3","_4":"_4","_4_0":"_4_0"},{"_0":"_0"},{"_0":"_0","_0_0":"_0_0"},{"_0":"_0"},{"_0":"_0","_1":"_1"}]', true);
$this->assertEquals($aExpected, $aRes);
} }
} }

View File

@@ -0,0 +1,243 @@
<?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>itop-structure</module>
<module>itop-config-mgmt</module>
<module>itop-attachments</module>
<module>itop-profiles-itil</module>
<module>itop-welcome-itil</module>
<module>itop-tickets</module>
<module>itop-files-information</module>
<module>combodo-db-tools</module>
<module>itop-core-update</module>
<module>itop-hub-connector</module>
<module>itop-oauth-client</module>
<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>itop-themes-compat</module>
<module>combodo-my-account</module>
<module>combodo-my-account-user-info</module>
<module>combodo-oauth2-client</module>
<module>itop-attribute-class-set</module>
<module>itop-attribute-encrypted-password</module>
<module>itop-ui-copypaste</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>
<sub_options>
<options type="array">
<choice>
<extension_code>itop-container-mgmt</extension_code>
<title>Containerization</title>
<description><![CDATA[Manage Container Images, Applications and Hosts]]></description>
<modules type="array">
<module>itop-container-mgmt</module>
</modules>
<default>false</default>
</choice>
</options>
</sub_options>
</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 to track "Problems" in iTop.</description>
<modules type="array">
<module>itop-problem-mgmt</module>
</modules>
</choice>
</options>
</step>
</steps>
</installation>