Compare commits

...

8 Commits

Author SHA1 Message Date
Timmy38
e0ef950664 N°9906 Add tests 2026-09-16 10:44:32 +02:00
Timmy38
8bdb7dd03a N°9906 Fix selected flag 2026-09-15 10:44:55 +02:00
Timmy38
f1d0432220 N°9906 Code clean up 2026-09-15 10:40:44 +02:00
Timmy38
34d26cde6c N°9906 Code clean up 2026-09-15 10:40:43 +02:00
Timmy38
0c4f208ca8 N°9906 Updating phpunit tests 2026-09-15 10:40:43 +02:00
Timmy38
538cf46d5f N°9906 Fix missing $ and phpunit tests 2026-09-15 10:40:43 +02:00
Timmy38
f9b66e88ec N°9906 Fix CS 2026-09-15 10:40:42 +02:00
Timmy38
a81c9a0be4 N°9906 Warn user that an extension is part of package 2026-09-15 10:40:42 +02:00
26 changed files with 310 additions and 33 deletions

View File

@@ -38,6 +38,7 @@ class DataFeatureRemovalController extends Controller
private array $aCountClassesToCleanup = [];
private array $aAnalysisDataTable = [];
private array $aDeletionExecutionSummary = [];
private ?array $aBasePackageModules = null;
private int $iCount = 0;
private int $iColumnCount = 2;
@@ -371,18 +372,23 @@ class DataFeatureRemovalController extends Controller
foreach ($aExtensionsRef as $oExtension) {
/** @var \iTopExtension $oExtension */
$aMetaData = [$oExtension->sVersion, $oExtension->GetExtensionSourceLabel(), $oExtension->sCode];
$bIsPackageExtension = $this->IsIncludedInPackage($oExtension);
$aExtensionsData[$oExtension->sCode] = [
'version' => $oExtension->sVersion,
'label' => $oExtension->sLabel,
'code' => $oExtension->sCode,
'description' => $oExtension->sDescription,
'source' => $oExtension->GetExtensionSourceLabel(),
'metadata' => $aMetaData,
'installed' => $oExtension->bInstalled,
'extra_flags' => [
'uninstallable' => $oExtension->CanBeUninstalled(),
'remote' => $oExtension->IsRemote(),
'missing' => $oExtension->bRemovedFromDisk,
'dependency_issue' => $oExtension->HasDependencyIssue(),
'already_part_of_itop' => $bIsPackageExtension,
],
];
@@ -391,6 +397,52 @@ class DataFeatureRemovalController extends Controller
return $aExtensionsData;
}
/**
* Returns true when all modules of a non-package extension are already included in base package modules.
*/
public function IsIncludedInPackage(iTopExtension $oExtension): bool
{
if ($oExtension->sSource === iTopExtension::SOURCE_WIZARD) {
return false;
}
$aModules = $oExtension->aModules ?? [];
if (!is_array($aModules) || empty($aModules)) {
return false;
}
$aBasePackageModules = $this->GetBasePackageModules();
foreach ($aModules as $sModuleId) {
if (!in_array($sModuleId, $aBasePackageModules)) {
return false;
}
}
return true;
}
public function GetBasePackageModules(): array
{
if ($this->aBasePackageModules !== null) {
return $this->aBasePackageModules;
}
$oConfig = MetaModel::GetConfig();
$oExtensionsMap = DataFeatureRemoverExtensionService::GetInstance()->GetExtensionMap();
$aSelectedExtensions = $oExtensionsMap->GetSelectedExtensions($oConfig);
foreach ($aSelectedExtensions as $index => $sExtensionCode) {
$oExtension = $oExtensionsMap->GetFromExtensionCode($sExtensionCode);
if ($oExtension->sSource !== iTopExtension::SOURCE_WIZARD) {
unset($aSelectedExtensions[$index]);
}
}
$sSourceEnv = MetaModel::GetEnvironment();
$oRuntimeEnvironment = new RunTimeEnvironment($sSourceEnv, false);
$this->aBasePackageModules = $oRuntimeEnvironment->GetModulesToLoadFromSelectedExtensions($oConfig, $aSelectedExtensions);
return $this->aBasePackageModules;
}
private function GetExtensionsDiff(array $aAddedExtensions, array $aRemovedExtensions): array
{
$aExtensions = [];

View File

@@ -32,9 +32,9 @@
{% UIColumn Standard {} %}
{% for aExtension in aAvailableExtensions[iColumnIndex] %}
{% if aExtension['installed'] %}
{% UIExtensionDetails Installed { sCode : aExtension['code'], sLabel : aExtension['label'], sDescription : aExtension['description'], aMetaData : [aExtension['version'], aExtension['source'], aExtension['code']], aExtraFlags : aExtension['extra_flags']} %}{% EndUIExtensionDetails %}
{% UIExtensionDetails Installed { sCode : aExtension['code'], sLabel : aExtension['label'], sDescription : aExtension['description'], aMetaData : aExtension['metadata'], aExtraFlags : aExtension['extra_flags']} %}{% EndUIExtensionDetails %}
{% else %}
{% UIExtensionDetails NotInstalled { sCode : aExtension['code'], sLabel : aExtension['label'], sDescription : aExtension['description'], aMetaData : [aExtension['version'], aExtension['source'], aExtension['code']], aExtraFlags : aExtension['extra_flags']} %}{% EndUIExtensionDetails %}
{% UIExtensionDetails NotInstalled { sCode : aExtension['code'], sLabel : aExtension['label'], sDescription : aExtension['description'], aMetaData : aExtension['metadata'], aExtraFlags : aExtension['extra_flags']} %}{% EndUIExtensionDetails %}
{% endif %}
{% endfor %}
{% EndUIColumn %}

View File

@@ -12,9 +12,9 @@
{% UIColumn Standard {} %}
{% for aExtension in aAvailableExtensions[iColumnIndex] %}
{% if aExtension['installed'] %}
{% UIExtensionDetails Installed { sCode : aExtension['code'], sLabel : aExtension['label'], sDescription : aExtension['description'], aMetaData : [aExtension['version'], aExtension['source'], aExtension['code']], aExtraFlags : aExtension['extra_flags']} %}{% EndUIExtensionDetails %}
{% UIExtensionDetails Installed { sCode : aExtension['code'], sLabel : aExtension['label'], sDescription : aExtension['description'], aMetaData : aExtension['metadata'], aExtraFlags : aExtension['extra_flags']} %}{% EndUIExtensionDetails %}
{% else %}
{% UIExtensionDetails NotInstalled { sCode : aExtension['code'], sLabel : aExtension['label'], sDescription : aExtension['description'], aMetaData : [aExtension['version'], aExtension['source'], aExtension['code']], aExtraFlags : aExtension['extra_flags']} %}{% EndUIExtensionDetails %}
{% UIExtensionDetails NotInstalled { sCode : aExtension['code'], sLabel : aExtension['label'], sDescription : aExtension['description'], aMetaData : aExtension['metadata'], aExtraFlags : aExtension['extra_flags']} %}{% EndUIExtensionDetails %}
{% endif %}
{% endfor %}
{% EndUIColumn %}

View File

@@ -23,6 +23,8 @@ Dict::Add('CS CZ', 'Czech', 'Čeština', [
'UI:Layout:ExtensionsDetails:BadgeToBeUninstalled+' => 'This extension will be uninstalled during the setup.~~',
'UI:Layout:ExtensionsDetails:BadgeNotUninstallable' => 'cannot be uninstalled~~',
'UI:Layout:ExtensionsDetails:BadgeNotUninstallable+' => 'Once this extension has been installed, it should not be uninstalled.~~',
'UI:Layout:ExtensionsDetails:BadgeAlreadyPartOfITop' => 'already part of iTop~~',
'UI:Layout:ExtensionsDetails:BadgeAlreadyPartOfITop+' => 'All modules in this extension are already part of the iTop package. To uninstall this extension, you have to delete its folder.~~',
'UI:Layout:ExtensionsDetails:BadgeMissingFromDisk' => 'missing from disk~~',
'UI:Layout:ExtensionsDetails:BadgeMissingFromDisk+' => 'The local extension folder has been removed from the disk. This will force the uninstallation of this extension.~~',
'UI:Layout:ExtensionsDetails:MenuAboutTitle' => 'About %1$s~~',

View File

@@ -23,6 +23,8 @@ Dict::Add('DA DA', 'Danish', 'Dansk', [
'UI:Layout:ExtensionsDetails:BadgeToBeUninstalled+' => 'This extension will be uninstalled during the setup.~~',
'UI:Layout:ExtensionsDetails:BadgeNotUninstallable' => 'cannot be uninstalled~~',
'UI:Layout:ExtensionsDetails:BadgeNotUninstallable+' => 'Once this extension has been installed, it should not be uninstalled.~~',
'UI:Layout:ExtensionsDetails:BadgeAlreadyPartOfITop' => 'already part of iTop~~',
'UI:Layout:ExtensionsDetails:BadgeAlreadyPartOfITop+' => 'All modules in this extension are already part of the iTop package. To uninstall this extension, you have to delete its folder.~~',
'UI:Layout:ExtensionsDetails:BadgeMissingFromDisk' => 'missing from disk~~',
'UI:Layout:ExtensionsDetails:BadgeMissingFromDisk+' => 'The local extension folder has been removed from the disk. This will force the uninstallation of this extension.~~',
'UI:Layout:ExtensionsDetails:MenuAboutTitle' => 'About %1$s~~',

View File

@@ -23,6 +23,8 @@ Dict::Add('DE DE', 'German', 'Deutsch', [
'UI:Layout:ExtensionsDetails:BadgeToBeUninstalled+' => 'Diese Extension wird während des Setups deinstalliert.',
'UI:Layout:ExtensionsDetails:BadgeNotUninstallable' => 'nicht deinstallierbar',
'UI:Layout:ExtensionsDetails:BadgeNotUninstallable+' => 'Sobald diese Extension installiert ist, sollte sie nicht mehr deinstalliert werden.',
'UI:Layout:ExtensionsDetails:BadgeAlreadyPartOfITop' => 'already part of iTop~~',
'UI:Layout:ExtensionsDetails:BadgeAlreadyPartOfITop+' => 'All modules in this extension are already part of the iTop package. To uninstall this extension, you have to delete its folder.~~',
'UI:Layout:ExtensionsDetails:BadgeMissingFromDisk' => 'auf dem Datenträger nicht vorhanden',
'UI:Layout:ExtensionsDetails:BadgeMissingFromDisk+' => 'Das lokale Verzeichnis der Extension wurde vom Datenträger entfernt. Dadurch wird die Deinstallation dieser Extension erzwungen.',
'UI:Layout:ExtensionsDetails:MenuAboutTitle' => 'Über %1$s',

View File

@@ -21,6 +21,8 @@ Dict::Add('EN US', 'English', 'English', [
'UI:Layout:ExtensionsDetails:BadgeToBeUninstalled+' => 'This extension will be uninstalled during the setup.',
'UI:Layout:ExtensionsDetails:BadgeNotUninstallable' => 'cannot be uninstalled',
'UI:Layout:ExtensionsDetails:BadgeNotUninstallable+' => 'Once this extension has been installed, it should not be uninstalled.',
'UI:Layout:ExtensionsDetails:BadgeAlreadyPartOfITop' => 'already part of iTop',
'UI:Layout:ExtensionsDetails:BadgeAlreadyPartOfITop+' => 'All modules in this extension are already part of the iTop package. To uninstall this extension, you have to delete its folder.',
'UI:Layout:ExtensionsDetails:BadgeMissingFromDisk' => 'missing from disk',
'UI:Layout:ExtensionsDetails:BadgeMissingFromDisk+' => 'The local extension folder has been removed from the disk. This will force the uninstallation of this extension.',
'UI:Layout:ExtensionsDetails:MenuAboutTitle' => 'About %1$s',

View File

@@ -23,6 +23,8 @@ Dict::Add('EN GB', 'British English', 'British English', [
'UI:Layout:ExtensionsDetails:BadgeToBeUninstalled+' => 'This extension will be uninstalled during the setup.~~',
'UI:Layout:ExtensionsDetails:BadgeNotUninstallable' => 'cannot be uninstalled~~',
'UI:Layout:ExtensionsDetails:BadgeNotUninstallable+' => 'Once this extension has been installed, it should not be uninstalled.~~',
'UI:Layout:ExtensionsDetails:BadgeAlreadyPartOfITop' => 'already part of iTop~~',
'UI:Layout:ExtensionsDetails:BadgeAlreadyPartOfITop+' => 'All modules in this extension are already part of the iTop package. To uninstall this extension, you have to delete its folder.~~',
'UI:Layout:ExtensionsDetails:BadgeMissingFromDisk' => 'missing from disk~~',
'UI:Layout:ExtensionsDetails:BadgeMissingFromDisk+' => 'The local extension folder has been removed from the disk. This will force the uninstallation of this extension.~~',
'UI:Layout:ExtensionsDetails:MenuAboutTitle' => 'About %1$s~~',

View File

@@ -23,6 +23,8 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellano', [
'UI:Layout:ExtensionsDetails:BadgeToBeUninstalled+' => 'This extension will be uninstalled during the setup.~~',
'UI:Layout:ExtensionsDetails:BadgeNotUninstallable' => 'cannot be uninstalled~~',
'UI:Layout:ExtensionsDetails:BadgeNotUninstallable+' => 'Once this extension has been installed, it should not be uninstalled.~~',
'UI:Layout:ExtensionsDetails:BadgeAlreadyPartOfITop' => 'already part of iTop~~',
'UI:Layout:ExtensionsDetails:BadgeAlreadyPartOfITop+' => 'All modules in this extension are already part of the iTop package. To uninstall this extension, you have to delete its folder.~~',
'UI:Layout:ExtensionsDetails:BadgeMissingFromDisk' => 'missing from disk~~',
'UI:Layout:ExtensionsDetails:BadgeMissingFromDisk+' => 'The local extension folder has been removed from the disk. This will force the uninstallation of this extension.~~',
'UI:Layout:ExtensionsDetails:MenuAboutTitle' => 'About %1$s~~',

View File

@@ -23,6 +23,8 @@ Dict::Add('FR FR', 'French', 'Français', [
'UI:Layout:ExtensionsDetails:BadgeToBeUninstalled+' => 'Cette extension sera désinstallée lors de l\'installation.',
'UI:Layout:ExtensionsDetails:BadgeNotUninstallable' => 'non désinstallable',
'UI:Layout:ExtensionsDetails:BadgeNotUninstallable+' => 'Une fois cette extension installée, elle ne devrait pas être désinstallée.',
'UI:Layout:ExtensionsDetails:BadgeAlreadyPartOfITop' => 'déjà dans iTop',
'UI:Layout:ExtensionsDetails:BadgeAlreadyPartOfITop+' => 'Tous les modules de cette extension font déjà partie du package iTop. Pour désinstaller cette extension, vous devez supprimer son dossier.',
'UI:Layout:ExtensionsDetails:BadgeMissingFromDisk' => 'supprimée du disque',
'UI:Layout:ExtensionsDetails:BadgeMissingFromDisk+' => 'Le dossier local de l\'extension a été supprimé du disque. Cela forcera la désinstallation de cette extension.',
'UI:Layout:ExtensionsDetails:MenuAboutTitle' => 'À propos de %1$s',

View File

@@ -23,6 +23,8 @@ Dict::Add('HU HU', 'Hungarian', 'Magyar', [
'UI:Layout:ExtensionsDetails:BadgeToBeUninstalled+' => 'This extension will be uninstalled during the setup.~~',
'UI:Layout:ExtensionsDetails:BadgeNotUninstallable' => 'cannot be uninstalled~~',
'UI:Layout:ExtensionsDetails:BadgeNotUninstallable+' => 'Once this extension has been installed, it should not be uninstalled.~~',
'UI:Layout:ExtensionsDetails:BadgeAlreadyPartOfITop' => 'already part of iTop~~',
'UI:Layout:ExtensionsDetails:BadgeAlreadyPartOfITop+' => 'All modules in this extension are already part of the iTop package. To uninstall this extension, you have to delete its folder.~~',
'UI:Layout:ExtensionsDetails:BadgeMissingFromDisk' => 'missing from disk~~',
'UI:Layout:ExtensionsDetails:BadgeMissingFromDisk+' => 'The local extension folder has been removed from the disk. This will force the uninstallation of this extension.~~',
'UI:Layout:ExtensionsDetails:MenuAboutTitle' => 'About %1$s~~',

View File

@@ -23,6 +23,8 @@ Dict::Add('IT IT', 'Italian', 'Italiano', [
'UI:Layout:ExtensionsDetails:BadgeToBeUninstalled+' => 'This extension will be uninstalled during the setup.~~',
'UI:Layout:ExtensionsDetails:BadgeNotUninstallable' => 'cannot be uninstalled~~',
'UI:Layout:ExtensionsDetails:BadgeNotUninstallable+' => 'Once this extension has been installed, it should not be uninstalled.~~',
'UI:Layout:ExtensionsDetails:BadgeAlreadyPartOfITop' => 'already part of iTop~~',
'UI:Layout:ExtensionsDetails:BadgeAlreadyPartOfITop+' => 'All modules in this extension are already part of the iTop package. To uninstall this extension, you have to delete its folder.~~',
'UI:Layout:ExtensionsDetails:BadgeMissingFromDisk' => 'missing from disk~~',
'UI:Layout:ExtensionsDetails:BadgeMissingFromDisk+' => 'The local extension folder has been removed from the disk. This will force the uninstallation of this extension.~~',
'UI:Layout:ExtensionsDetails:MenuAboutTitle' => 'About %1$s~~',

View File

@@ -23,6 +23,8 @@ Dict::Add('JA JP', 'Japanese', '日本語', [
'UI:Layout:ExtensionsDetails:BadgeToBeUninstalled+' => 'This extension will be uninstalled during the setup.~~',
'UI:Layout:ExtensionsDetails:BadgeNotUninstallable' => 'cannot be uninstalled~~',
'UI:Layout:ExtensionsDetails:BadgeNotUninstallable+' => 'Once this extension has been installed, it should not be uninstalled.~~',
'UI:Layout:ExtensionsDetails:BadgeAlreadyPartOfITop' => 'already part of iTop~~',
'UI:Layout:ExtensionsDetails:BadgeAlreadyPartOfITop+' => 'All modules in this extension are already part of the iTop package. To uninstall this extension, you have to delete its folder.~~',
'UI:Layout:ExtensionsDetails:BadgeMissingFromDisk' => 'missing from disk~~',
'UI:Layout:ExtensionsDetails:BadgeMissingFromDisk+' => 'The local extension folder has been removed from the disk. This will force the uninstallation of this extension.~~',
'UI:Layout:ExtensionsDetails:MenuAboutTitle' => 'About %1$s~~',

View File

@@ -23,6 +23,8 @@ Dict::Add('NL NL', 'Dutch', 'Nederlands', [
'UI:Layout:ExtensionsDetails:BadgeToBeUninstalled+' => 'This extension will be uninstalled during the setup.~~',
'UI:Layout:ExtensionsDetails:BadgeNotUninstallable' => 'cannot be uninstalled~~',
'UI:Layout:ExtensionsDetails:BadgeNotUninstallable+' => 'Once this extension has been installed, it should not be uninstalled.~~',
'UI:Layout:ExtensionsDetails:BadgeAlreadyPartOfITop' => 'already part of iTop~~',
'UI:Layout:ExtensionsDetails:BadgeAlreadyPartOfITop+' => 'All modules in this extension are already part of the iTop package. To uninstall this extension, you have to delete its folder.~~',
'UI:Layout:ExtensionsDetails:BadgeMissingFromDisk' => 'missing from disk~~',
'UI:Layout:ExtensionsDetails:BadgeMissingFromDisk+' => 'The local extension folder has been removed from the disk. This will force the uninstallation of this extension.~~',
'UI:Layout:ExtensionsDetails:MenuAboutTitle' => 'About %1$s~~',

View File

@@ -23,6 +23,8 @@ Dict::Add('PL PL', 'Polish', 'Polski', [
'UI:Layout:ExtensionsDetails:BadgeToBeUninstalled+' => 'This extension will be uninstalled during the setup.~~',
'UI:Layout:ExtensionsDetails:BadgeNotUninstallable' => 'cannot be uninstalled~~',
'UI:Layout:ExtensionsDetails:BadgeNotUninstallable+' => 'Once this extension has been installed, it should not be uninstalled.~~',
'UI:Layout:ExtensionsDetails:BadgeAlreadyPartOfITop' => 'already part of iTop~~',
'UI:Layout:ExtensionsDetails:BadgeAlreadyPartOfITop+' => 'All modules in this extension are already part of the iTop package. To uninstall this extension, you have to delete its folder.~~',
'UI:Layout:ExtensionsDetails:BadgeMissingFromDisk' => 'missing from disk~~',
'UI:Layout:ExtensionsDetails:BadgeMissingFromDisk+' => 'The local extension folder has been removed from the disk. This will force the uninstallation of this extension.~~',
'UI:Layout:ExtensionsDetails:MenuAboutTitle' => 'About %1$s~~',

View File

@@ -23,6 +23,8 @@ Dict::Add('PT BR', 'Brazilian', 'Brazilian', [
'UI:Layout:ExtensionsDetails:BadgeToBeUninstalled+' => 'This extension will be uninstalled during the setup.~~',
'UI:Layout:ExtensionsDetails:BadgeNotUninstallable' => 'cannot be uninstalled~~',
'UI:Layout:ExtensionsDetails:BadgeNotUninstallable+' => 'Once this extension has been installed, it should not be uninstalled.~~',
'UI:Layout:ExtensionsDetails:BadgeAlreadyPartOfITop' => 'already part of iTop~~',
'UI:Layout:ExtensionsDetails:BadgeAlreadyPartOfITop+' => 'All modules in this extension are already part of the iTop package. To uninstall this extension, you have to delete its folder.~~',
'UI:Layout:ExtensionsDetails:BadgeMissingFromDisk' => 'missing from disk~~',
'UI:Layout:ExtensionsDetails:BadgeMissingFromDisk+' => 'The local extension folder has been removed from the disk. This will force the uninstallation of this extension.~~',
'UI:Layout:ExtensionsDetails:MenuAboutTitle' => 'About %1$s~~',

View File

@@ -23,6 +23,8 @@ Dict::Add('RU RU', 'Russian', 'Русский', [
'UI:Layout:ExtensionsDetails:BadgeToBeUninstalled+' => 'Это расширение будет удалено в ходе установки.',
'UI:Layout:ExtensionsDetails:BadgeNotUninstallable' => 'нельзя удалить',
'UI:Layout:ExtensionsDetails:BadgeNotUninstallable+' => 'После установки это расширение не должно удаляться.',
'UI:Layout:ExtensionsDetails:BadgeAlreadyPartOfITop' => 'already part of iTop~~',
'UI:Layout:ExtensionsDetails:BadgeAlreadyPartOfITop+' => 'All modules in this extension are already part of the iTop package. To uninstall this extension, you have to delete its folder.~~',
'UI:Layout:ExtensionsDetails:BadgeMissingFromDisk' => 'отсутствует на диске',
'UI:Layout:ExtensionsDetails:BadgeMissingFromDisk+' => 'Локальная папка расширения удалена с диска. Это приведёт к принудительному удалению расширения.',
'UI:Layout:ExtensionsDetails:MenuAboutTitle' => 'О расширении %1$s',

View File

@@ -23,6 +23,8 @@ Dict::Add('SK SK', 'Slovak', 'Slovenčina', [
'UI:Layout:ExtensionsDetails:BadgeToBeUninstalled+' => 'This extension will be uninstalled during the setup.~~',
'UI:Layout:ExtensionsDetails:BadgeNotUninstallable' => 'cannot be uninstalled~~',
'UI:Layout:ExtensionsDetails:BadgeNotUninstallable+' => 'Once this extension has been installed, it should not be uninstalled.~~',
'UI:Layout:ExtensionsDetails:BadgeAlreadyPartOfITop' => 'already part of iTop~~',
'UI:Layout:ExtensionsDetails:BadgeAlreadyPartOfITop+' => 'All modules in this extension are already part of the iTop package. To uninstall this extension, you have to delete its folder.~~',
'UI:Layout:ExtensionsDetails:BadgeMissingFromDisk' => 'missing from disk~~',
'UI:Layout:ExtensionsDetails:BadgeMissingFromDisk+' => 'The local extension folder has been removed from the disk. This will force the uninstallation of this extension.~~',
'UI:Layout:ExtensionsDetails:MenuAboutTitle' => 'About %1$s~~',

View File

@@ -23,6 +23,8 @@ Dict::Add('TR TR', 'Turkish', 'Türkçe', [
'UI:Layout:ExtensionsDetails:BadgeToBeUninstalled+' => 'This extension will be uninstalled during the setup.~~',
'UI:Layout:ExtensionsDetails:BadgeNotUninstallable' => 'cannot be uninstalled~~',
'UI:Layout:ExtensionsDetails:BadgeNotUninstallable+' => 'Once this extension has been installed, it should not be uninstalled.~~',
'UI:Layout:ExtensionsDetails:BadgeAlreadyPartOfITop' => 'already part of iTop~~',
'UI:Layout:ExtensionsDetails:BadgeAlreadyPartOfITop+' => 'All modules in this extension are already part of the iTop package. To uninstall this extension, you have to delete its folder.~~',
'UI:Layout:ExtensionsDetails:BadgeMissingFromDisk' => 'missing from disk~~',
'UI:Layout:ExtensionsDetails:BadgeMissingFromDisk+' => 'The local extension folder has been removed from the disk. This will force the uninstallation of this extension.~~',
'UI:Layout:ExtensionsDetails:MenuAboutTitle' => 'About %1$s~~',

View File

@@ -23,6 +23,8 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [
'UI:Layout:ExtensionsDetails:BadgeToBeUninstalled+' => 'This extension will be uninstalled during the setup.~~',
'UI:Layout:ExtensionsDetails:BadgeNotUninstallable' => 'cannot be uninstalled~~',
'UI:Layout:ExtensionsDetails:BadgeNotUninstallable+' => 'Once this extension has been installed, it should not be uninstalled.~~',
'UI:Layout:ExtensionsDetails:BadgeAlreadyPartOfITop' => 'already part of iTop~~',
'UI:Layout:ExtensionsDetails:BadgeAlreadyPartOfITop+' => 'All modules in this extension are already part of the iTop package. To uninstall this extension, you have to delete its folder.~~',
'UI:Layout:ExtensionsDetails:BadgeMissingFromDisk' => 'missing from disk~~',
'UI:Layout:ExtensionsDetails:BadgeMissingFromDisk+' => 'The local extension folder has been removed from the disk. This will force the uninstallation of this extension.~~',
'UI:Layout:ExtensionsDetails:MenuAboutTitle' => 'About %1$s~~',

View File

@@ -753,7 +753,7 @@ class iTopExtensionsMap
* @param string[] $aRemovedExtensions
* @return string[] :
*/
public function GetSelectedExtensions(Config $oConfig, array $aAddedExtensions, array $aRemovedExtensions): array
public function GetSelectedExtensions(Config $oConfig, array $aAddedExtensions = [], array $aRemovedExtensions = []): array
{
$aDbChoices = $this->GetChoicesFromDatabase($oConfig);
foreach ($aDbChoices as $i => $sChoice) {

View File

@@ -169,6 +169,36 @@ class WizStepModulesChoice extends AbstractWizStepInstall
return new WizardState(WizStepModulesChoice::class, (string)($index - 1));
}
protected ?array $aSelectedModules = null;
public function GetAllSelectedModulesUntilNow(): array
{
if (!is_null($this->aSelectedModules)) {
return $this->aSelectedModules;
}
$this->aSelectedModules = [];
$aSelectedChoices = json_decode($this->oWizard->GetParameter('selected_components', '{}'), true);
$iNextStep = $this->GetStepIndex();
$index = $iNextStep;
while (isset($aSelectedChoices[$iNextStep])) {
//Let's empty the next steps, we only want what has been chosen before
$aSelectedChoices[$iNextStep] = [];
$iNextStep++;
}
$aPreviousSteps = [];
for ($i = 0; $i < $index; $i++) {
$aStepInfo = $this->GetStepInfo($i);
$this->GetSelectedModules($aStepInfo, $aSelectedChoices[$i], $this->aSelectedModules);
$aPreviousSteps = array_merge($aPreviousSteps, $this->aSelectedModules);
}
return $this->aSelectedModules;
}
public function GetWizardSteps(): array
{
$aSteps = [
@@ -761,6 +791,22 @@ EOF
return $this->aSteps[$index] ?? null;
}
public function ExtensionIsAlreadyIncludedInPreviousChoices(?iTopExtension $oITopExtension): bool
{
if (is_null($oITopExtension) || empty($oITopExtension->aModules)) {
return false;
}
$aAllPreviousChoicesModules = $this->GetAllSelectedModulesUntilNow();
foreach ($oITopExtension->aModules as $sModuleId) {
if (!isset($aAllPreviousChoicesModules[$sModuleId])) {
return false;
}
}
return true;
}
public function ComputeChoiceFlags(array $aChoice, string $sChoiceId, array $aSelectedComponents, bool $bAllDisabled, bool $bDisableUninstallCheck): array
{
if (array_key_exists($sChoiceId, $this->aFlagsByChoiceId)) {
@@ -777,6 +823,7 @@ EOF
$bDependencyIssue = $oITopExtension?->HasDependencyIssue() ?? false;
$bIsRemoteExtension = $oITopExtension?->sSource === iTopExtension::SOURCE_REMOTE;
$bIsPackageExtension = $oITopExtension?->sSource === iTopExtension::SOURCE_WIZARD;
$bAlreadyIncluded = $this->ExtensionIsAlreadyIncludedInPreviousChoices($oITopExtension);
$bDoNotUninstall = !$bCanBeUninstalled || $bIsRemoteExtension;
$bChecked = $bSelected;
@@ -788,6 +835,9 @@ EOF
} elseif ($bMandatory && $bIsPackageExtension) {
$bDisabled = true;
$bChecked = true;
} elseif ($bAlreadyIncluded) {
$bDisabled = true;
$bChecked = true;
} else {
if ($bDependencyIssue) {
// If the extension has a dependency issue, it cannot be checked and must be unchecked using the "force-uninstall" option
@@ -823,6 +873,7 @@ EOF
$aFlags = [
'uninstallable' => $bCanBeUninstalled,
'already_included' => $bAlreadyIncluded,
'dependency_issue' => $bDependencyIssue,
'mandatory' => $bMandatory,
'missing' => $bMissingFromDisk,
@@ -922,7 +973,7 @@ EOF
<a class="setup--wizard-choice--more-info" target="_blank" href="'.$aChoice['more_info'].'">
<i class="setup-extension--icon fas fa-external-link-alt" title="More information"></i>
</a>' : '';
$sDescription = isset($aChoice['description']) ? utils::EscapeHtml($aChoice['description']) : '';
$sDescription = isset($aChoice['description']) ? trim(utils::EscapeHtml($aChoice['description'])) : '';
$sId = utils::EscapeHtml($aChoice['extension_code']);
$sDataId = 'data-id="'.utils::EscapeHtml($aChoice['extension_code']).'"';
$sDisabled = $aFlags['disabled'] ? ' disabled data-disabled="disabled"' : '';
@@ -947,11 +998,15 @@ EOF
if ($aFlags['dependency_issue']) {
$sTooltip .= '<div id="badge--'.$sId.'--cannot-be-installed" class="ibo-badge ibo-block ibo-is-orange" title="This extension cannot be installed because one or more dependencies are not satisfied." >cannot be installed</div>';
}
if ($aFlags['already_included']) {
$sTooltip .= '<div id="badge--'.$sId.'--already-part-of-itop" class="ibo-badge ibo-block ibo-is-yellow" title="All modules in this extension are already part of the iTop package. To uninstall this extension, you have to delete its folder." >already part of iTop</div>';
}
$sMetadata = '';
if (isset($aChoice['version']) && isset($aChoice['source_label'])) {
$sMetadata = '<span>v'.$aChoice['version'].'</span><span>'.$aChoice['source_label'].'</span><span>'.$aChoice['extension_code'].'</span>';
}
$sChoiceDisabled = $aFlags['disabled'] && !$aFlags['checked'] ? 'choice-disabled' : '';
$oPage->add('

View File

@@ -18,6 +18,7 @@ class ExtensionDetailsUIBlockFactory extends AbstractUIBlockFactory
private const BADGE_ID_TO_BE_INSTALLED = 'to-be-installed';
private const BADGE_ID_TO_BE_UNINSTALLED = 'to-be-uninstalled';
private const BADGE_ID_NOT_UNINSTALLABLE = 'not-uninstallable';
private const BADGE_ID_ALREADY_PART_OF_ITOP = 'already-part-of-itop';
private const BADGE_ID_MISSING_FROM_DISK = 'missing-from-disk';
private const BADGE_ID_CANNOT_BE_INSTALLED = 'cannot-be-installed';
@@ -27,10 +28,11 @@ class ExtensionDetailsUIBlockFactory extends AbstractUIBlockFactory
$bUninstallable = $aExtraFlags['uninstallable'] ?? true;
$bMissingFromDisk = $aExtraFlags['missing'] ?? false;
$bDependencyIssue = $aExtraFlags['dependency_issue'] ?? false;
$bAlreadyPartOfITop = $aExtraFlags['already_part_of_itop'] ?? false;
$bSelected = $aExtraFlags['selected'] ?? true;
$bDisabled = $aExtraFlags['disabled'] ?? false;
$bRemote = $aExtraFlags['remote'] ?? false;
self::AddExtraBadges($aBadges, $bUninstallable, $bMissingFromDisk, $bDependencyIssue, $sCode);
self::AddExtraBadges($aBadges, $bUninstallable, $bMissingFromDisk, $bDependencyIssue, $bAlreadyPartOfITop, $sCode);
$oBadgeInstalled = BadgeUIBlockFactory::MakeGreen(
Dict::S('UI:Layout:ExtensionsDetails:BadgeInstalled'),
Dict::S('UI:Layout:ExtensionsDetails:BadgeInstalled+'),
@@ -51,6 +53,10 @@ class ExtensionDetailsUIBlockFactory extends AbstractUIBlockFactory
if ($bMissingFromDisk || $bDependencyIssue) {
$oExtensionDetails->GetToggler()->SetIsToggled(false);
$oExtensionDetails->GetToggler()->SetIsDisabled(true);
} elseif ($bAlreadyPartOfITop) {
$bSelected = true;
$oExtensionDetails->GetToggler()->SetIsToggled(true);
$oExtensionDetails->GetToggler()->SetIsDisabled(true);
} elseif ((!$bUninstallable || $bRemote) && !$bDisabled) {
$oExtensionDetails->AllowForceUninstall();
$oExtensionDetails->GetToggler()->SetIsDisabled(true);
@@ -72,9 +78,10 @@ class ExtensionDetailsUIBlockFactory extends AbstractUIBlockFactory
$aBadges = [];
$bUninstallable = $aExtraFlags['uninstallable'] ?? true;
$bDependencyIssue = $aExtraFlags['dependency_issue'] ?? false;
$bAlreadyPartOfITop = $aExtraFlags['already_part_of_itop'] ?? false;
$bSelected = $aExtraFlags['selected'] ?? false;
$bDisabled = $aExtraFlags['disabled'] ?? false;
self::AddExtraBadges($aBadges, $bUninstallable, false, $bDependencyIssue, $sCode);
self::AddExtraBadges($aBadges, $bUninstallable, false, $bDependencyIssue, $bAlreadyPartOfITop, $sCode);
$oBadgeInstalled = BadgeUIBlockFactory::MakeGrey(
Dict::S('UI:Layout:ExtensionsDetails:BadgeNotInstalled'),
Dict::S('UI:Layout:ExtensionsDetails:BadgeNotInstalled+'),
@@ -94,6 +101,9 @@ class ExtensionDetailsUIBlockFactory extends AbstractUIBlockFactory
if ($bDependencyIssue) {
$oExtensionDetails->GetToggler()->SetIsToggled(false);
$oExtensionDetails->GetToggler()->SetIsDisabled(true);
} elseif ($bAlreadyPartOfITop) {
$oExtensionDetails->GetToggler()->SetIsToggled(true);
$oExtensionDetails->GetToggler()->SetIsDisabled(true);
}
if ($bSelected) {
@@ -107,7 +117,7 @@ class ExtensionDetailsUIBlockFactory extends AbstractUIBlockFactory
return $oExtensionDetails;
}
private static function AddExtraBadges(array &$aBadges, bool $bUninstallable, bool $bMissingFromDisk, bool $bDependencyIssue, string $sCode)
private static function AddExtraBadges(array &$aBadges, bool $bUninstallable, bool $bMissingFromDisk, bool $bDependencyIssue, bool $bAlreadyPartOfITop, string $sCode)
{
if (!$bUninstallable) {
$aBadges[] = BadgeUIBlockFactory::MakeYellow(
@@ -130,6 +140,13 @@ class ExtensionDetailsUIBlockFactory extends AbstractUIBlockFactory
self::GetBadgeId($sCode, self::BADGE_ID_CANNOT_BE_INSTALLED)
);
}
if ($bAlreadyPartOfITop) {
$aBadges[] = BadgeUIBlockFactory::MakeYellow(
Dict::S('UI:Layout:ExtensionsDetails:BadgeAlreadyPartOfITop'),
Dict::S('UI:Layout:ExtensionsDetails:BadgeAlreadyPartOfITop+'),
self::GetBadgeId($sCode, self::BADGE_ID_ALREADY_PART_OF_ITOP)
);
}
}
/**

View File

@@ -9,6 +9,7 @@ namespace Combodo\iTop\Test\UnitTest\Module\DataFeatureRemoval\Service;
use Combodo\iTop\DataFeatureRemoval\Controller\DataFeatureRemovalController;
use Combodo\iTop\Test\UnitTest\ItopDataTestCase;
use iTopExtension;
/**
* @see DataFeatureController
@@ -32,4 +33,49 @@ class DataFeatureControllerTest extends ItopDataTestCase
$expected = '{"0":"itop-container-mgmt","1":"combodo-monitoring"}';
$this->assertEquals($expected, $this->InvokeNonPublicMethod(DataFeatureRemovalController::class, 'ConvertIntoSetupFormat', $oController, [ $aExtensions]));
}
public function testGetBasePackageModules()
{
$oController = new DataFeatureRemovalController();
$aModules = $oController->GetBasePackageModules();
$this->assertNotEmpty($aModules);
$this->assertContains('itop-structure', $aModules);
$this->assertContains('authent-local', $aModules);
$this->assertContains('itop-backup', $aModules);
$this->assertContains('itop-tickets', $aModules);
}
public function testIsIncludedInPackage()
{
$oController = $this->getMockBuilder(DataFeatureRemovalController::class)
->onlyMethods(['GetBasePackageModules'])
->getMock();
$oController->method('GetBasePackageModules')->willReturn([
'itop-structure',
'authent-local',
'itop-backup',
'itop-tickets',
]);
$oIncludedExtension = new iTopExtension();
$oIncludedExtension->sSource = iTopExtension::SOURCE_MANUAL;
$oIncludedExtension->aModules = ['itop-structure', 'authent-local'];
$this->assertTrue($oController->IsIncludedInPackage($oIncludedExtension), 'IsIncludedInPackage should return true if all modules are included in the base package');
$oNotIncludedExtension = new iTopExtension();
$oNotIncludedExtension->sSource = iTopExtension::SOURCE_MANUAL;
$oNotIncludedExtension->aModules = ['itop-structure', 'combodo-non-existing-module'];
$this->assertFalse($oController->IsIncludedInPackage($oNotIncludedExtension), 'IsIncludedInPackage should return false if at least one of its modules is not included in the base package');
$oEmptyExtension = new iTopExtension();
$oEmptyExtension->sSource = iTopExtension::SOURCE_MANUAL;
$oEmptyExtension->aModules = [];
$this->assertFalse($oController->IsIncludedInPackage($oEmptyExtension), 'IsIncludedInPackage should return false if the extension has no modules');
$oPackageExtension = new iTopExtension();
$oPackageExtension->sSource = iTopExtension::SOURCE_WIZARD;
$oPackageExtension->aModules = ['itop-structure', 'authent-local'];
$this->assertFalse($oController->IsIncludedInPackage($oPackageExtension), 'IsIncludedInPackage should return false if the extension is a package extension');
}
}

View File

@@ -55,6 +55,7 @@ class WizStepModulesChoiceTest extends ItopTestCase
'dependency_issue' => false,
'mandatory' => false,
'remote' => false,
'already_included' => false,
],
],
'#node1 - A missing extension should always be disabled and unchecked, even a mandatory extension included in package' => [
@@ -81,6 +82,7 @@ class WizStepModulesChoiceTest extends ItopTestCase
'dependency_issue' => false,
'mandatory' => true,
'remote' => false,
'already_included' => false,
],
],
'#node2 - A mandatory extension included in package should be checked and disabled even if the "disable uninstallation check" flag is set' => [
@@ -106,6 +108,7 @@ class WizStepModulesChoiceTest extends ItopTestCase
'dependency_issue' => false,
'mandatory' => true,
'remote' => false,
'already_included' => false,
],
],
'#node3 - An installed non uninstallable sub extension should force its parent to be checked and disabled' => [
@@ -140,6 +143,7 @@ class WizStepModulesChoiceTest extends ItopTestCase
'dependency_issue' => false,
'mandatory' => false,
'remote' => false,
'already_included' => false,
],
],
'#node3 - An installed remote sub extension should force its parent to be checked and disabled' => [
@@ -175,6 +179,7 @@ class WizStepModulesChoiceTest extends ItopTestCase
'dependency_issue' => false,
'mandatory' => false,
'remote' => false,
'already_included' => false,
],
],
'#node4 - An installed uninstallable sub extension should force its parent to be checked but not disabled' => [
@@ -209,6 +214,7 @@ class WizStepModulesChoiceTest extends ItopTestCase
'dependency_issue' => false,
'mandatory' => false,
'remote' => false,
'already_included' => false,
],
],
'#node4 - An installed non uninstallable sub extension should force its parent to be checked but not disabled if the "disable uninstallation check" flag is set' => [
@@ -243,6 +249,7 @@ class WizStepModulesChoiceTest extends ItopTestCase
'dependency_issue' => false,
'mandatory' => false,
'remote' => false,
'already_included' => false,
],
],
'#node4 - An installed remote sub extension should force its parent to be checked but not disabled if the "disable uninstallation check" flag is set' => [
@@ -278,6 +285,7 @@ class WizStepModulesChoiceTest extends ItopTestCase
'dependency_issue' => false,
'mandatory' => false,
'remote' => false,
'already_included' => false,
],
],
'#node5 - A non installed extension with missing dependencies should be not checked and disabled' => [
@@ -305,6 +313,7 @@ class WizStepModulesChoiceTest extends ItopTestCase
'dependency_issue' => true,
'mandatory' => false,
'remote' => false,
'already_included' => false,
],
],
'#node5 - A non installed extension with missing dependencies should be not checked and disabled even with force uninstall' => [
@@ -332,6 +341,7 @@ class WizStepModulesChoiceTest extends ItopTestCase
'dependency_issue' => true,
'mandatory' => false,
'remote' => false,
'already_included' => false,
],
],
'#node6 - An installed extension with missing dependencies and without force uninstall should be checked and disabled' => [
@@ -359,6 +369,7 @@ class WizStepModulesChoiceTest extends ItopTestCase
'dependency_issue' => true,
'mandatory' => false,
'remote' => false,
'already_included' => false,
],
],
'#node7 - An installed extension with missing dependencies and with force uninstall should be checked and enabled' => [
@@ -386,6 +397,7 @@ class WizStepModulesChoiceTest extends ItopTestCase
'dependency_issue' => true,
'mandatory' => false,
'remote' => false,
'already_included' => false,
],
],
'#node8 - An installed but not selected extension with missing dependencies and with force uninstall should be unchecked and enabled' => [
@@ -413,6 +425,7 @@ class WizStepModulesChoiceTest extends ItopTestCase
'dependency_issue' => true,
'mandatory' => false,
'remote' => false,
'already_included' => false,
],
],
'#node9 - A not selected, not installed extension should not be checked and be enabled' => [
@@ -436,6 +449,7 @@ class WizStepModulesChoiceTest extends ItopTestCase
'dependency_issue' => false,
'mandatory' => false,
'remote' => false,
'already_included' => false,
],
],
'#node10 - A selected but not installed extension should be checked and enabled' => [
@@ -459,6 +473,7 @@ class WizStepModulesChoiceTest extends ItopTestCase
'dependency_issue' => false,
'mandatory' => false,
'remote' => false,
'already_included' => false,
],
],
'#node11 - An installed but not selected extension should not be checked and be enabled' => [
@@ -482,6 +497,7 @@ class WizStepModulesChoiceTest extends ItopTestCase
'dependency_issue' => false,
'mandatory' => false,
'remote' => false,
'already_included' => false,
],
],
'#node12 - An installed and selected extension should be checked and enabled' => [
@@ -506,6 +522,7 @@ class WizStepModulesChoiceTest extends ItopTestCase
'dependency_issue' => false,
'mandatory' => false,
'remote' => false,
'already_included' => false,
],
],
'#node13 - An installed non uninstallable extension should be checked and disabled' => [
@@ -529,6 +546,7 @@ class WizStepModulesChoiceTest extends ItopTestCase
'dependency_issue' => false,
'mandatory' => false,
'remote' => false,
'already_included' => false,
],
],
'#node13 - An installed remote extension should be checked and disabled' => [
@@ -553,6 +571,7 @@ class WizStepModulesChoiceTest extends ItopTestCase
'dependency_issue' => false,
'mandatory' => false,
'remote' => true,
'already_included' => false,
],
],
'#node14 - An installed but not selected non uninstallable extension should be checked and enabled if the "disable uninstallation check" flag is set' => [
@@ -576,6 +595,7 @@ class WizStepModulesChoiceTest extends ItopTestCase
'dependency_issue' => false,
'mandatory' => false,
'remote' => false,
'already_included' => false,
],
],
'#node14 - An installed but not selected remote extension should be checked and enabled if the "disable uninstallation check" flag is set' => [
@@ -600,6 +620,7 @@ class WizStepModulesChoiceTest extends ItopTestCase
'dependency_issue' => false,
'mandatory' => false,
'remote' => true,
'already_included' => false,
],
],
'#node15 - An installed non uninstallable extension should be checked and enabled if the "disable uninstallation check" flag is set' => [
@@ -623,6 +644,7 @@ class WizStepModulesChoiceTest extends ItopTestCase
'dependency_issue' => false,
'mandatory' => false,
'remote' => false,
'already_included' => false,
],
],
'#node15 - An installed remote extension should be checked and enabled if the "disable uninstallation check" flag is set' => [
@@ -647,9 +669,10 @@ class WizStepModulesChoiceTest extends ItopTestCase
'dependency_issue' => false,
'mandatory' => false,
'remote' => true,
'already_included' => false,
],
],
'#node16 - A non installed non uninstallable sub extension should not force its parent flags' => [
'#node16 - A non installed sub extension should not force its parent flags' => [
'aExtensionsOnDiskOrDb' => [
'itop-ext1' => [
'installed' => true,
@@ -681,6 +704,7 @@ class WizStepModulesChoiceTest extends ItopTestCase
'dependency_issue' => false,
'mandatory' => false,
'remote' => false,
'already_included' => false,
],
],
'#node16 - A non installed remote sub extension should not force its parent flags' => [
@@ -716,6 +740,7 @@ class WizStepModulesChoiceTest extends ItopTestCase
'dependency_issue' => false,
'mandatory' => false,
'remote' => false,
'already_included' => false,
],
],
'#node13 - An installed non uninstallable extension should be checked and disabled when the "disable uninstallation check" flag is not set, even if it is not selected' => [
@@ -739,6 +764,7 @@ class WizStepModulesChoiceTest extends ItopTestCase
'dependency_issue' => false,
'mandatory' => false,
'remote' => false,
'already_included' => false,
],
],
'#node6 - An installed extension with missing dependencies without force uninstall should be checked and disabled, even if it is not selected' => [
@@ -766,6 +792,7 @@ class WizStepModulesChoiceTest extends ItopTestCase
'dependency_issue' => true,
'mandatory' => false,
'remote' => false,
'already_included' => false,
],
],
];
@@ -781,6 +808,52 @@ class WizStepModulesChoiceTest extends ItopTestCase
$this->assertEquals($aExpectedFlags, $aFlags);
}
/**
* #node17 an extension with all of its modules already selected in previous steps should be checked and disabled, with the "already_included" flag set to true
*/
public function testComputeChoiceFlagsShouldReturnAlreadyIncludedWhenAllModulesAreInPreviousSteps(): void
{
$this->oWizard->SetParameter('source_dir', __DIR__.'/ressources');
$this->oWizard->SetParameter('selected_components', json_encode([
['_0' => '_0'],//Selecting itop-config-mgmt-core
]));
$oWizStepModulesChoice = new WizStepModulesChoiceFake($this->oWizard, '1');
$oWizStepModulesChoice->setExtensionMap(iTopExtensionsMapFake::createFromArray([
'itop-ext1' => [
'installed' => false,
'modules' => [
'itop-config-mgmt',
'itop-attachments',
],
],
]));
$aFlags = $oWizStepModulesChoice->ComputeChoiceFlags(
[
'extension_code' => 'itop-ext1',
'uninstallable' => true,
],
'_0',
[],
false,
false
);
$aExpectedFlags = [
'uninstallable' => true,
'missing' => false,
'installed' => false,
'disabled' => true,
'checked' => true,
'dependency_issue' => false,
'mandatory' => false,
'remote' => false,
'already_included' => true,
];
$this->assertEquals($aExpectedFlags, $aFlags);
}
public function ProviderCanMoveForwardFromChoiceFlags(): array
{
return [

View File

@@ -4,30 +4,30 @@ Removed from disk
Yes : Forced uninstallation, unchecked & disabled .color_red #node1
No : Mandatory & part of package
Yes : Forced installation, checked & disabled .color_orange #node2
No : Has sub options
Yes : Sub option checked
No : Modules included in package
Yes : Forced installation, checked & disabled .color_orange #node17
No : Has sub options & Sub option checked
Yes : Sub option disabled
Yes : checked & disabled .color_orange #node3
No : force checked .color_yellow #node4
#node16 No : (Dependency issue)
No : Dependency issue
Yes : Installed
No : unchecked & disabled .color_orange #node5
Yes : Force uninstall
No : checked & disabled .color_orange #node6
Yes : Selected
Yes : checked & enabled .color_green #node7
No : unchecked & enabled .color_green #node8
No : Installed
No : Selected
No : unchecked & enabled .color_green #node9
Yes : checked & enabled .color_green #node10
Yes : Remote | cannot_be_uninstalled
No : Selected
No : unchecked & enabled .color_green #node11
Yes : checked & enabled .color_green #node12
No : checked & enabled .color_green #node4
No : Dependency issue #node16
Yes : Installed
No : unchecked & disabled .color_orange #node5
Yes : Force uninstall
No : checked & disabled .color_orange #node13
No : checked & disabled .color_orange #node6
Yes : Selected
No : unchecked & enabled .color_green #node14
Yes : checked & enabled .color_green #node15
Yes : checked & enabled .color_green #node7
No : unchecked & enabled .color_green #node8
No : Installed
No : Selected
No : unchecked & enabled .color_green #node9
Yes : checked & enabled .color_green #node10
Yes : Remote | cannot_be_uninstalled
No : Selected
No : unchecked & enabled .color_green #node11
Yes : checked & enabled .color_green #node12
Yes : Force uninstall
No : checked & disabled .color_orange #node13
Yes : Selected
No : unchecked & enabled .color_green #node14
Yes : checked & enabled .color_green #node15