diff --git a/setup/wizardsteps.class.inc.php b/setup/wizardsteps.class.inc.php index d80f2d146f..148da3011e 100644 --- a/setup/wizardsteps.class.inc.php +++ b/setup/wizardsteps.class.inc.php @@ -1562,7 +1562,7 @@ EOF } } - $aAlternatives = isset($aInfo['alternatives']) ? $aInfo['alternatives'] : []; + $aAlternatives = $aInfo['alternatives'] ?? []; $sChoiceName = null; foreach ($aAlternatives as $index => $aChoice) { $sChoiceId = $sParentId.self::$SEP.$index; @@ -1953,10 +1953,47 @@ EOF return ''; } + public function ComputeChoiceFlags(array $aChoice, string $sChoiceId, array $aSelectedComponents, bool $bAllDisabled, bool $bDisableUninstallCheck, bool $bUpgradeMode) + { + $oITopExtension = $this->oExtensionsMap->GetFromExtensionCode($aChoice['extension_code']); + $bCanBeUninstalled = isset($aChoice['uninstallable']) ? $aChoice['uninstallable'] === true || $aChoice['uninstallable'] === 'yes' : $oITopExtension->CanBeUninstalled(); + $bSelected = isset($aSelectedComponents[$sChoiceId]) && ($aSelectedComponents[$sChoiceId] == $sChoiceId); + $bMandatory = (isset($aChoice['mandatory']) && $aChoice['mandatory']) || $bUpgradeMode && $oITopExtension->bInstalled && !$bCanBeUninstalled && !$bDisableUninstallCheck; + + $bMissingFromDisk = isset($aChoice['missing']) && $aChoice['missing'] === true; + $bInstalled = $bMissingFromDisk || $oITopExtension->bInstalled; + $bDisabled = $bMandatory || $bAllDisabled || $bMissingFromDisk; + $bChecked = $bMandatory || $bSelected; + + if (isset($aChoice['sub_options'])) { + $aOptions = $aChoice['sub_options']['options'] ?? []; + foreach ($aOptions as $index => $aSubChoice) { + $sSubChoiceId = $sChoiceId.self::$SEP.$index; + $aSubFlags = $this->ComputeChoiceFlags($aSubChoice, $sSubChoiceId, $aSelectedComponents, $bAllDisabled, $bDisableUninstallCheck, $bUpgradeMode); + if ($aSubFlags['checked']) { + $bChecked = true; + if ($aSubFlags['disabled']) { + //If some sub options are enabled and cannot be disabled, this choice should also cannot be disabled since it would disable all its sub options + $bDisabled = true; + } + } + + } + } + + return [ + 'uninstallable' => $bCanBeUninstalled, + 'missing' => $bMissingFromDisk, + 'installed' => $bInstalled, + 'disabled' => $bDisabled, + 'checked' => $bChecked, + ]; + } + protected function DisplayOptions($oPage, $aStepInfo, $aSelectedComponents, $aDefaults, $sParentId = '', $bAllDisabled = false) { - $aOptions = isset($aStepInfo['options']) ? $aStepInfo['options'] : []; - $aAlternatives = isset($aStepInfo['alternatives']) ? $aStepInfo['alternatives'] : []; + $aOptions = $aStepInfo['options'] ?? []; + $aAlternatives = $aStepInfo['alternatives'] ?? []; $bDisableUninstallCheck = (bool)$this->oWizard->GetParameter('force-uninstall', false); @@ -1964,43 +2001,33 @@ EOF $sChoiceId = $sParentId.self::$SEP.$index; $sDataId = 'data-id="'.utils::EscapeHtml($aChoice['extension_code']).'"'; $sId = utils::EscapeHtml($aChoice['extension_code']); - $bIsDefault = array_key_exists($sChoiceId, $aDefaults); - - $oITopExtension = $this->oExtensionsMap->GetFromExtensionCode($aChoice['extension_code']); - $bCanBeUninstalled = isset($aChoice['uninstallable']) ? $aChoice['uninstallable'] : $oITopExtension->CanBeUninstalled(); - $bSelected = isset($aSelectedComponents[$sChoiceId]) && ($aSelectedComponents[$sChoiceId] == $sChoiceId); - $bMandatory = (isset($aChoice['mandatory']) && $aChoice['mandatory']) || $this->bUpgrade && $bIsDefault && !$bCanBeUninstalled && !$bDisableUninstallCheck; - ; - $bMissingFromDisk = isset($aChoice['missing']) && $aChoice['missing'] === true; - $bInstalled = $bMissingFromDisk || $oITopExtension->bInstalled; - $bDisabled = $bMandatory || $bAllDisabled || $bMissingFromDisk; - $bChecked = $bMandatory || $bSelected; + $aFlags = static::ComputeChoiceFlags($aChoice, $sChoiceId, $aSelectedComponents, $bAllDisabled, $bDisableUninstallCheck, $this->bUpgrade); $sTooltip = ''; $sUnremovable = ''; - if ($bMissingFromDisk) { + if ($aFlags['missing']) { $sTooltip .= 'source removed'; } - if ($bInstalled) { + if ($aFlags['installed']) { $sTooltip .= 'installed'; $sTooltip .= 'to be uninstalled'; } else { $sTooltip .= 'to be installed'; $sTooltip .= 'not installed'; } - if (!$bCanBeUninstalled) { + if (!$aFlags['uninstallable']) { $sTooltip .= 'cannot be uninstalled'; } - if ($bDisabled && !$bChecked && !$bCanBeUninstalled && !$bDisableUninstallCheck) { + if ($aFlags['disabled'] && !$aFlags['checked'] && !$aFlags['uninstallable'] && !$bDisableUninstallCheck) { $this->bCanMoveForward = false;//Disable "Next" } - $sChecked = $bChecked ? ' checked ' : ''; - $sDisabled = $bDisabled ? ' disabled data-disabled="disabled" ' : ''; - $sMissingModule = $bMissingFromDisk ? 'setup-extension--missing' : ''; + $sChecked = $aFlags['checked'] ? ' checked ' : ''; + $sDisabled = $aFlags['disabled'] ? ' disabled data-disabled="disabled" ' : ''; + $sMissingModule = $aFlags['missing'] ? 'setup-extension--missing' : ''; - $sHiddenInput = $bDisabled && $bChecked ? '' : ''; + $sHiddenInput = $aFlags['disabled'] && $aFlags['checked'] ? '' : ''; $oPage->add('
'.$sHiddenInput.' '); - $this->DisplayChoice($oPage, $aChoice, $aSelectedComponents, $aDefaults, $sChoiceId, $bDisabled, $sTooltip); + $this->DisplayChoice($oPage, $aChoice, $aSelectedComponents, $aDefaults, $sChoiceId, $aFlags['disabled'], $sTooltip); $oPage->add('
'); } $sChoiceName = null; @@ -2035,7 +2062,6 @@ EOF $sChoiceId = $sParentId.self::$SEP.$index; $sDataId = 'data-id="'.utils::EscapeHtml($aChoice['extension_code']).'"'; $sId = utils::EscapeHtml($aChoice['extension_code']); - if ($sChoiceName == null) { $sChoiceName = $sChoiceId; // All radios share the same name } diff --git a/tests/php-unit-tests/unitary-tests/setup/WizStepModulesChoiceFake.php b/tests/php-unit-tests/unitary-tests/setup/WizStepModulesChoiceFake.php new file mode 100644 index 0000000000..69a99b3dd1 --- /dev/null +++ b/tests/php-unit-tests/unitary-tests/setup/WizStepModulesChoiceFake.php @@ -0,0 +1,14 @@ +oExtensionsMap = $oMap; + } +} diff --git a/tests/php-unit-tests/unitary-tests/setup/WizStepModulesChoiceTest.php b/tests/php-unit-tests/unitary-tests/setup/WizStepModulesChoiceTest.php new file mode 100644 index 0000000000..11a11fd46c --- /dev/null +++ b/tests/php-unit-tests/unitary-tests/setup/WizStepModulesChoiceTest.php @@ -0,0 +1,260 @@ +RequireOnceItopFile('/setup/unattended-install/InstallationFileService.php'); + require_once __DIR__.'/iTopExtensionsMapFake.php'; + require_once __DIR__.'/WizStepModulesChoiceFake.php'; + + $this->oStep = new \WizStepModulesChoiceFake(new WizardController('', ''), ''); + ModuleDiscovery::ResetCache(); + } + + public function ProviderComputeChoiceFlags() + { + return [ + 'selected but not installed extension' => [ + 'aExtensions' => [ + 'itop-ext1' => [ + 'installed' => false, + ], + ], + 'bUpgrade' => false, + 'bDisableUninstallCheck' => false, + 'sChoiceId' => '_0', + 'aStepInfo' => [ + 'extension_code' => 'itop-ext1', + 'mandatory' => false, + 'uninstallable' => true, + ], + 'aSelected' => ['_0' => '_0'], + 'aExpectedFlags' => [ + 'uninstallable' => true, + 'missing' => false, + 'installed' => false, + 'disabled' => false, + 'checked' => true, + ], + ], + 'not selected, not installed extension' => [ + 'aExtensions' => [ + 'itop-ext1' => [ + 'installed' => false, + ], + ], + 'bUpgrade' => true, + 'bDisableUninstallCheck' => false, + 'sChoiceId' => '_0', + 'aStepInfo' => [ + 'extension_code' => 'itop-ext1', + 'mandatory' => false, + 'uninstallable' => true, + ], + 'aSelected' => [], + 'aExpectedFlags' => [ + 'uninstallable' => true, + 'missing' => false, + 'installed' => false, + 'disabled' => false, + 'checked' => false, + ], + ], + 'installed extension' => [ + 'aExtensions' => [ + 'itop-ext1' => [ + 'installed' => true, + ], + ], + 'bUpgrade' => true, + 'bDisableUninstallCheck' => false, + 'sChoiceId' => '_0', + 'aStepInfo' => [ + 'extension_code' => 'itop-ext1', + 'mandatory' => false, + 'uninstallable' => true, + ], + 'aSelected' => [], + 'aExpectedFlags' => [ + 'uninstallable' => true, + 'missing' => false, + 'installed' => true, + 'disabled' => false, + 'checked' => false, + ], + ], + 'installed non uninstallable extension' => [ + 'aExtensions' => [ + 'itop-ext1' => [ + 'installed' => true, + ], + ], + 'bUpgrade' => true, + 'bDisableUninstallCheck' => false, + 'sChoiceId' => '_0', + 'aStepInfo' => [ + 'extension_code' => 'itop-ext1', + 'mandatory' => false, + 'uninstallable' => false, + ], + 'aSelected' => [], + 'aExpectedFlags' => [ + 'uninstallable' => false, + 'missing' => false, + 'installed' => true, + 'disabled' => true, + 'checked' => true, + ], + ], + 'mandatory extension' => [ + 'aExtensions' => [ + 'itop-ext1' => [ + 'installed' => false, + ], + ], + 'bUpgrade' => true, + 'bDisableUninstallCheck' => false, + 'sChoiceId' => '_0', + 'aStepInfo' => [ + 'extension_code' => 'itop-ext1', + 'mandatory' => true, + 'uninstallable' => true, + ], + 'aSelected' => [], + 'aExpectedFlags' => [ + 'uninstallable' => true, + 'missing' => false, + 'installed' => false, + 'disabled' => true, + 'checked' => true, + ], + ], + 'optional sub extension' => [ + 'aExtensions' => [ + 'itop-ext1' => [ + 'installed' => false, + ], + 'itop-ext1-1' => [ + 'installed' => false, + ], + ], + 'bUpgrade' => true, + 'bDisableUninstallCheck' => false, + 'sChoiceId' => '_0', + 'aStepInfo' => [ + 'extension_code' => 'itop-ext1', + 'mandatory' => false, + 'uninstallable' => true, + 'sub_options' => [ + 'options' => [ + [ + 'extension_code' => 'itop-ext1-1', + 'mandatory' => false, + 'uninstallable' => true, + ], + ], + ], + ], + 'aSelected' => [], + 'aExpectedFlags' => [ + 'uninstallable' => true, + 'missing' => false, + 'installed' => false, + 'disabled' => false, + 'checked' => false, + ], + ], + 'mandatory sub extension' => [ + 'aExtensions' => [ + 'itop-ext1' => [ + 'installed' => false, + ], + 'itop-ext1-1' => [ + 'installed' => false, + ], + ], + 'bUpgrade' => true, + 'bDisableUninstallCheck' => false, + 'sChoiceId' => '_0', + 'aStepInfo' => [ + 'extension_code' => 'itop-ext1', + 'mandatory' => false, + 'uninstallable' => true, + 'sub_options' => [ + 'options' => [ + [ + 'extension_code' => 'itop-ext1-1', + 'mandatory' => true, + 'uninstallable' => true, + ], + ], + ], + ], + 'aSelected' => [], + 'aExpectedFlags' => [ + 'uninstallable' => true, + 'missing' => false, + 'installed' => false, + 'disabled' => true, + 'checked' => true, + ], + ], + 'non uninstallable sub extension' => [ + 'aExtensions' => [ + 'itop-ext1' => [ + 'installed' => true, + ], + 'itop-ext1-1' => [ + 'installed' => true, + ], + ], + 'bUpgrade' => true, + 'bDisableUninstallCheck' => false, + 'sChoiceId' => '_0', + 'aStepInfo' => [ + 'extension_code' => 'itop-ext1', + 'mandatory' => false, + 'uninstallable' => true, + 'sub_options' => [ + 'options' => [ + [ + 'extension_code' => 'itop-ext1-1', + 'mandatory' => false, + 'uninstallable' => false, + ], + ], + ], + ], + 'aSelected' => [], + 'aExpectedFlags' => [ + 'uninstallable' => true, + 'missing' => false, + 'installed' => true, + 'disabled' => true, + 'checked' => true, + ], + ], + ]; + } + + /** + * @dataProvider ProviderComputeChoiceFlags + */ + public function testComputeChoiceFlags($aExtensions, $bUpgrade, $bDisableUninstallCheck, $sChoiceId, $aStepInfo, $aSelected, $aExpectedFlags) + { + $this->oStep->setExtensionMap(iTopExtensionsMapFake::createFromArray($aExtensions)); + $aFlags = $this->oStep->ComputeChoiceFlags($aStepInfo, $sChoiceId, $aSelected, false, $bDisableUninstallCheck, $bUpgrade); + $this->assertEquals($aExpectedFlags, $aFlags); + } + +} diff --git a/tests/php-unit-tests/unitary-tests/setup/iTopExtensionsMapFake.php b/tests/php-unit-tests/unitary-tests/setup/iTopExtensionsMapFake.php new file mode 100644 index 0000000000..e80b88eb6e --- /dev/null +++ b/tests/php-unit-tests/unitary-tests/setup/iTopExtensionsMapFake.php @@ -0,0 +1,27 @@ +aExtensions = []; + $this->aExtensionsByCode = []; + $this->aScannedDirs = []; + } + + public static function createFromArray($aExtensions) + { + $oMap = new static(); + foreach ($aExtensions as $sCode => $aExtension) { + $oExtension = new iTopExtension(); + $oExtension->sCode = $sCode; + $oExtension->bInstalled = $aExtension['installed']; + $oExtension->aModules = $aExtension['modules'] ?? []; + $oExtension->bCanBeUninstalled = $aExtension['uninstallable'] ?? null; + $oExtension->sVersion = $aExtension['version'] ?? '1.0.0'; + $oExtension->aModuleInfo = []; + $oMap->AddExtension($oExtension); + } + return $oMap; + } +}