From 109aa03901ff760847751451d9aedbc9c7f03566 Mon Sep 17 00:00:00 2001 From: odain Date: Mon, 4 May 2026 16:16:01 +0200 Subject: [PATCH 1/6] =?UTF-8?q?N=C2=B09412=20-=20Backend=20cleanup=20must?= =?UTF-8?q?=20work=20on=20setup=20settings=20that=20differ=20from=20runtim?= =?UTF-8?q?e=20-=20WIP?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../DataFeatureRemovalController.php | 45 +++++++++++++++++++ .../templates/AnalysisResult.html.twig | 29 ++++++++++++ setup/wizardsteps/WizStepDataAudit.php | 6 ++- setup/wizardsteps/WizStepModulesChoice.php | 1 + 4 files changed, 79 insertions(+), 2 deletions(-) create mode 100644 datamodels/2.x/combodo-data-feature-removal/templates/AnalysisResult.html.twig diff --git a/datamodels/2.x/combodo-data-feature-removal/src/Controller/DataFeatureRemovalController.php b/datamodels/2.x/combodo-data-feature-removal/src/Controller/DataFeatureRemovalController.php index f7059412d5..dfdd3d98c3 100644 --- a/datamodels/2.x/combodo-data-feature-removal/src/Controller/DataFeatureRemovalController.php +++ b/datamodels/2.x/combodo-data-feature-removal/src/Controller/DataFeatureRemovalController.php @@ -22,6 +22,7 @@ use Dict; use Exception; use IssueLog; use MetaModel; +use SetupUtils; use utils; class DataFeatureRemovalController extends Controller @@ -97,6 +98,50 @@ class DataFeatureRemovalController extends Controller $this->aCountClassesToCleanup = $aGetRemovedClasses; } + public function OperationAnalysisResult(): void + { + $aParams = []; + + if (SetupUtils::IsSessionSetupTokenValid()) { + //from setup wizard/mtp + SetupUtils::EraseSetupToken(); + } else { + //from same module + $this->ValidateTransactionId(); + } + + $sSourceEnv = MetaModel::GetEnvironment(); + $oSetupAudit = new SetupAudit($sSourceEnv); + $aGetRemovedClasses = array_keys($oSetupAudit->RunDataAudit()); + IssueLog::Debug(__METHOD__, null, ['aGetRemovedClasses' => $aGetRemovedClasses]); + + $oDataCleanupService = new DataCleanupService(); + $aDeletionPlanSummaryEntities = $oDataCleanupService->GetCleanupSummary($aGetRemovedClasses); + $aColumns = ['Class', 'DeleteCount' , 'UpdateCount', 'IssueCount']; + $aRows = []; + $iQueryCount = 0; + $bHasIssues = false; + foreach ($aDeletionPlanSummaryEntities as $oDeletionPlanSummaryEntity) { + $aRows[] = [ + $oDeletionPlanSummaryEntity->sClass, + $oDeletionPlanSummaryEntity->iDeleteCount, + $oDeletionPlanSummaryEntity->iUpdateCount, + $oDeletionPlanSummaryEntity->iIssueCount, + ]; + $bHasIssues |= ($oDeletionPlanSummaryEntity->iIssueCount !== 0); + $iQueryCount += $oDeletionPlanSummaryEntity->iDeleteCount; + $iQueryCount += $oDeletionPlanSummaryEntity->iUpdateCount; + } + + $aParams['sTransactionId'] = utils::GetNewTransactionId(); + $aParams['aDeletionPlanSummary'] = $this->GetTableData('Extensions', $aColumns, $aRows); + $aParams['aClasses'] = $aGetRemovedClasses; + $aParams['iQueryCount'] = $iQueryCount; + $aParams['bDeletionPossible'] = !$bHasIssues; + + $this->DisplayPage($aParams); + } + public function OperationDeletionPlan(): void { $aParams = []; diff --git a/datamodels/2.x/combodo-data-feature-removal/templates/AnalysisResult.html.twig b/datamodels/2.x/combodo-data-feature-removal/templates/AnalysisResult.html.twig new file mode 100644 index 0000000000..c37bff85a6 --- /dev/null +++ b/datamodels/2.x/combodo-data-feature-removal/templates/AnalysisResult.html.twig @@ -0,0 +1,29 @@ +{# @copyright Copyright (C) 2010-2026 Combodo SARL #} +{# @license http://opensource.org/licenses/AGPL-3.0 #} + +{% UIPanel ForInformation { sTitle:'DataFeatureRemoval:DeletionPlan:Title'|dict_s, sSubTitle: 'DataFeatureRemoval:DeletionPlan:SubTitle'|dict_format(iQueryCount) } %} + {% UIDataTable ForForm { sRef:'aDeletionPlanSummary', aColumns:aDeletionPlanSummary.Columns, aData:aDeletionPlanSummary.Data} %}{% EndUIDataTable %} +{% EndUIPanel %} + +{% if bDeletionPossible %} + {% UIForm Standard {} %} + {% UIInput ForHidden { sName:'transaction_id', sValue:sTransactionId} %} + {% UIInput ForHidden { sName:'operation', sValue:'DoDeletion'} %} + {% for sKey, sClass in aClasses %} + {% UIInput ForHidden { sName:"classes[" ~ sKey ~ "]", sValue:sClass } %} + {% endfor %} + {% UIToolbar ForButton {} %} + {% UIButton ForPrimaryAction {sLabel:'UI:Button:DoDeletion'|dict_s, sName:'btn_deletion', sId:'btn_deletion', bIsSubmit:true} %} + {% EndUIToolbar %} + {% EndUIForm %} +{% else %} + {{ 'DataFeatureRemoval:DeletionPlan:Error:Issues'|dict_s }} +{% endif %} + +{% UIForm Standard {} %} + {% UIInput ForHidden { sName:'transaction_id', sValue:sTransactionId} %} + {% UIInput ForHidden { sName:'operation', sValue:'Main'} %} + {% UIToolbar ForButton {} %} + {% UIButton ForPrimaryAction {sLabel:'UI:Button:BackToMain'|dict_s, sName:'btn_back', sId:'btn_back', bIsSubmit:true} %} + {% EndUIToolbar %} +{% EndUIForm %} \ No newline at end of file diff --git a/setup/wizardsteps/WizStepDataAudit.php b/setup/wizardsteps/WizStepDataAudit.php index 43a1e8ce99..9ee68c2def 100644 --- a/setup/wizardsteps/WizStepDataAudit.php +++ b/setup/wizardsteps/WizStepDataAudit.php @@ -18,6 +18,7 @@ * You should have received a copy of the GNU Affero General Public License */ +use Combodo\iTop\Application\Helper\Session; use Combodo\iTop\Application\WebPage\WebPage; require_once(APPROOT.'setup/sequencers/DataAuditSequencer.php'); @@ -112,11 +113,12 @@ JS); INPUT; } - + $sUID = Session::Get('setup_token'); $oPage->add( << - + + $aHiddenRemovedExtensionInputs HTML diff --git a/setup/wizardsteps/WizStepModulesChoice.php b/setup/wizardsteps/WizStepModulesChoice.php index b7bb07884d..8474588d69 100644 --- a/setup/wizardsteps/WizStepModulesChoice.php +++ b/setup/wizardsteps/WizStepModulesChoice.php @@ -509,6 +509,7 @@ EOF $sDisplayChoices .= '
  • '.$aChoice['title'].'
  • '; if (isset($aChoice['modules'])) { if (count($aChoice['modules']) === 0 && (!isset($aChoice['missing']) || $aChoice['missing'] === false)) { + SetupLog::Error(__METHOD__, null, ['choice' => $aChoice]); throw new Exception('Extension '.$aChoice['extension_code'].' does not have any module associated'); } foreach ($aChoice['modules'] as $sModuleId) { From 99a5b47899806d60853322ac466c265856031d66 Mon Sep 17 00:00:00 2001 From: odain Date: Mon, 4 May 2026 16:39:09 +0200 Subject: [PATCH 2/6] =?UTF-8?q?N=C2=B09144=20-=20adapt=20unattended=20XMLs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../unattended-install/xml_setup/fresh-install.xml | 11 +++++++---- .../xml_setup/itil-fresh-install.xml | 11 +++++++---- setup/unattended-install/xml_setup/itil-upgrade.xml | 11 +++++++---- setup/unattended-install/xml_setup/upgrade.xml | 13 ++++++++----- 4 files changed, 29 insertions(+), 17 deletions(-) diff --git a/setup/unattended-install/xml_setup/fresh-install.xml b/setup/unattended-install/xml_setup/fresh-install.xml index db1311cf55..70bb87e801 100644 --- a/setup/unattended-install/xml_setup/fresh-install.xml +++ b/setup/unattended-install/xml_setup/fresh-install.xml @@ -1,11 +1,13 @@ install - - + + 1 + 1 + 1 + datamodels/2.x/ - 2.7.0 - /var/www/html/iTop/conf/production/config-itop.php + 3.3.0 extensions production @@ -38,4 +40,5 @@ + off diff --git a/setup/unattended-install/xml_setup/itil-fresh-install.xml b/setup/unattended-install/xml_setup/itil-fresh-install.xml index e1b1e594f0..b79c06b954 100644 --- a/setup/unattended-install/xml_setup/itil-fresh-install.xml +++ b/setup/unattended-install/xml_setup/itil-fresh-install.xml @@ -1,11 +1,13 @@ install - - + + 1 + 1 + 1 + datamodels/2.x/ - 2.7.0 - /var/www/html/iTop/conf/production/config-itop.php + 3.3.0 extensions production @@ -50,4 +52,5 @@ itop-config-mgmt-core itop-kown-error-mgmt + off diff --git a/setup/unattended-install/xml_setup/itil-upgrade.xml b/setup/unattended-install/xml_setup/itil-upgrade.xml index eb0a63208a..dfe5d729b0 100644 --- a/setup/unattended-install/xml_setup/itil-upgrade.xml +++ b/setup/unattended-install/xml_setup/itil-upgrade.xml @@ -1,11 +1,13 @@ upgrade - - + + 1 + 1 + 1 + datamodels/2.x/ - 2.7.0 - /var/www/html/iTop/conf/production/config-itop.php + 3.3.0 extensions production @@ -50,4 +52,5 @@ itop-config-mgmt-core itop-kown-error-mgmt + off diff --git a/setup/unattended-install/xml_setup/upgrade.xml b/setup/unattended-install/xml_setup/upgrade.xml index ff0d153f1e..deb1ce314c 100644 --- a/setup/unattended-install/xml_setup/upgrade.xml +++ b/setup/unattended-install/xml_setup/upgrade.xml @@ -1,11 +1,13 @@ upgrade - - + + 1 + 1 + 1 + datamodels/2.x/ - 2.7.0 - /var/www/html/iTop/conf/production/config-itop.php + 3.3.0 extensions production @@ -38,4 +40,5 @@ - \ No newline at end of file + off + From 73ebf663d601116d19a155b6c976050afa5dcd20 Mon Sep 17 00:00:00 2001 From: odain Date: Tue, 5 May 2026 16:46:49 +0200 Subject: [PATCH 3/6] =?UTF-8?q?N=C2=B09144=20-=20setup=20robustness?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ModelReflectionSerializer.php | 13 ---------- setup/modulediscovery/ModuleFileReader.php | 2 ++ setup/runtimeenv.class.inc.php | 24 +++++++++++++++---- .../ApplicationInstallSequencer.php | 2 ++ setup/sequencers/StepSequencer.php | 5 ++++ 5 files changed, 29 insertions(+), 17 deletions(-) diff --git a/setup/feature_removal/ModelReflectionSerializer.php b/setup/feature_removal/ModelReflectionSerializer.php index 8b04427b7d..53ed1d5dfa 100644 --- a/setup/feature_removal/ModelReflectionSerializer.php +++ b/setup/feature_removal/ModelReflectionSerializer.php @@ -36,19 +36,6 @@ class ModelReflectionSerializer { IssueLog::Info(__METHOD__, null, ['env' => $sEnv]); - $sCurrentEnvt = MetaModel::GetEnvironment(); - if ($sCurrentEnvt === $sEnv) { - $aClasses = MetaModel::GetClasses(); - if (count($aClasses) === 0) { - //MetaModel not started yet - $sConfFile = utils::GetConfigFilePath($sEnv); - - MetaModel::Startup($sConfFile, false /* $bModelOnly */, false /* $bAllowCache */, false /* $bTraceSourceFiles */, $sEnv); - $aClasses = MetaModel::GetClasses(); - } - return $aClasses; - } - $sPHPExec = trim(utils::GetConfig()->Get('php_path')); $sOutput = ""; $iRes = 0; diff --git a/setup/modulediscovery/ModuleFileReader.php b/setup/modulediscovery/ModuleFileReader.php index 9fbcd634c3..b87df88954 100644 --- a/setup/modulediscovery/ModuleFileReader.php +++ b/setup/modulediscovery/ModuleFileReader.php @@ -16,6 +16,7 @@ use PhpParser\Node\Stmt\ElseIf_; use PhpParser\Node\Stmt\Expression; use PhpParser\Node\Stmt\If_; use PhpParser\ParserFactory; +use SetupLog; require_once __DIR__.'/ModuleFileReaderException.php'; require_once APPROOT.'sources/PhpParser/Evaluation/PhpExpressionEvaluator.php'; @@ -143,6 +144,7 @@ class ModuleFileReader } // Replace the main function call by an assignment to a variable, as an array... $sModuleFileContents = str_replace(['SetupWebPage::AddModule', 'ModuleDiscovery::AddModule'], '$aModuleInfo = array', $sModuleFileContents); + SetupLog::Debug(__METHOD__, null, ['module_file' => $sModuleFilePath]); eval($sModuleFileContents); // Assigns $aModuleInfo if (count($aModuleInfo) === 0) { diff --git a/setup/runtimeenv.class.inc.php b/setup/runtimeenv.class.inc.php index 74d9667d9d..8d0ace6239 100644 --- a/setup/runtimeenv.class.inc.php +++ b/setup/runtimeenv.class.inc.php @@ -1126,7 +1126,9 @@ class RunTimeEnvironment } if (is_null($aSelectedModules) || in_array($sModuleId, $aSelectedModules)) { - $aArgs = [MetaModel::GetConfig(), $aModule['installed_version'], $aModule['available_version']]; + $oConfig = MetaModel::GetConfig(); + $oConfig->Set('access_mode', ACCESS_FULL); + $aArgs = [$oConfig, $aModule['installed_version'], $aModule['available_version']]; RunTimeEnvironment::CallInstallerHandler($aModule, $sHandlerName, $aArgs); } } @@ -1193,6 +1195,9 @@ class RunTimeEnvironment */ public function LoadData($aAvailableModules, $bSampleData, $aSelectedModules = null) { + $oConfig = MetaModel::GetConfig(); + $oConfig->Set('access_mode', ACCESS_FULL); + $oDataLoader = new XMLDataLoader(); CMDBObject::SetCurrentChangeFromParams("Initialization from XML files for the selected modules "); @@ -1326,9 +1331,6 @@ class RunTimeEnvironment { $oSetupAudit = new SetupAudit(ITOP_DEFAULT_ENV, $this->sBuildEnv); - //Make sure the MetaModel is started before analysing for issues - $sConfFile = utils::GetConfigFilePath(ITOP_DEFAULT_ENV); - MetaModel::Startup($sConfFile, false, false); // Start on production environment $oSetupAudit->RunDataAudit(true); $iCount = $oSetupAudit->GetDataToCleanupCount(); @@ -1537,6 +1539,15 @@ class RunTimeEnvironment $oBackup->CreateCompressedBackup($sTargetFile, $sSourceConfigFile); } + /** + * @param \Config $oConfig + * @return void + */ + public function EnterMaintenanceMode(Config $oConfig) + { + SetupUtils::EnterMaintenanceMode($oConfig); + } + public function EnterReadOnlyMode(Config $oConfig) { if ($this->GetFinalEnv() != 'production') { @@ -1550,6 +1561,11 @@ class RunTimeEnvironment SetupUtils::EnterReadOnlyMode($oConfig); } + public function ExitMaintenanceMode(): void + { + SetupUtils::ExitMaintenanceMode(); + } + public function ExitReadOnlyMode() { if ($this->GetFinalEnv() != 'production') { diff --git a/setup/sequencers/ApplicationInstallSequencer.php b/setup/sequencers/ApplicationInstallSequencer.php index fe68981fe5..bfa612d689 100644 --- a/setup/sequencers/ApplicationInstallSequencer.php +++ b/setup/sequencers/ApplicationInstallSequencer.php @@ -89,6 +89,7 @@ class ApplicationInstallSequencer extends StepSequencer return $this->ComputeNextStep($sStep); case 'migrate-before': + $this->oRunTimeEnvironment->EnterMaintenanceMode($this->GetConfig()); if (array_key_exists($sStep, $this->oParams->Get('optional_steps', []))) { $this->oRunTimeEnvironment->MigrateDataBeforeUpdateStructure($this->oParams->Get('mode'), $this->GetConfig()); } @@ -139,6 +140,7 @@ class ApplicationInstallSequencer extends StepSequencer case 'commit': $this->oRunTimeEnvironment->Commit(); $this->oRunTimeEnvironment->ExitReadOnlyMode(); + $this->oRunTimeEnvironment->ExitMaintenanceMode(); return $this->GetNextStep('', 'Completed', 100); default: diff --git a/setup/sequencers/StepSequencer.php b/setup/sequencers/StepSequencer.php index e80d986f12..bb7515299b 100644 --- a/setup/sequencers/StepSequencer.php +++ b/setup/sequencers/StepSequencer.php @@ -159,6 +159,11 @@ abstract class StepSequencer file_put_contents(APPROOT.'log/'.$sFileName.'.xml', $sSafeXml); } + /** + * @return \Config + * @throws \ConfigException + * @throws \CoreException + */ protected function GetConfig() { if (! is_null($this->oTestConfig)) { From 4d2722654a56c68cc3967c09f43b3475ec8b0391 Mon Sep 17 00:00:00 2001 From: odain Date: Wed, 6 May 2026 10:46:57 +0200 Subject: [PATCH 4/6] =?UTF-8?q?N=C2=B09144=20-=20no=20audit=20in=20unatten?= =?UTF-8?q?ded=20+=20tests?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- setup/runtimeenv.class.inc.php | 3 + setup/sequencers/DataAuditSequencer.php | 4 ++ setup/wizardsteps/AbstractWizStepInstall.php | 1 + .../ApplicationInstallerSequencerTest.php | 20 +++--- .../sequencers/DataAuditSequencerTest.php | 62 ++++++++++++++++++- 5 files changed, 80 insertions(+), 10 deletions(-) diff --git a/setup/runtimeenv.class.inc.php b/setup/runtimeenv.class.inc.php index 8d0ace6239..913c58ee80 100644 --- a/setup/runtimeenv.class.inc.php +++ b/setup/runtimeenv.class.inc.php @@ -1331,6 +1331,9 @@ class RunTimeEnvironment { $oSetupAudit = new SetupAudit(ITOP_DEFAULT_ENV, $this->sBuildEnv); + //Make sure the MetaModel is started before analysing for issues + $sConfFile = utils::GetConfigFilePath(ITOP_DEFAULT_ENV); + MetaModel::Startup($sConfFile, false, false); // Start on production environment $oSetupAudit->RunDataAudit(true); $iCount = $oSetupAudit->GetDataToCleanupCount(); diff --git a/setup/sequencers/DataAuditSequencer.php b/setup/sequencers/DataAuditSequencer.php index 36ab5fc209..eb302873d0 100644 --- a/setup/sequencers/DataAuditSequencer.php +++ b/setup/sequencers/DataAuditSequencer.php @@ -94,6 +94,10 @@ class DataAuditSequencer extends StepSequencer protected function IsDataAuditRequired(): bool { + if (! array_key_exists('setup-audit', $this->oParams->Get('optional_steps', []))) { + return false; + } + if ('install' === $this->oParams->Get('mode')) { return false; } diff --git a/setup/wizardsteps/AbstractWizStepInstall.php b/setup/wizardsteps/AbstractWizStepInstall.php index ab8860b385..8311488408 100644 --- a/setup/wizardsteps/AbstractWizStepInstall.php +++ b/setup/wizardsteps/AbstractWizStepInstall.php @@ -66,6 +66,7 @@ abstract class AbstractWizStepInstall extends WizardStep 'log-parameters' => true, 'migrate-before' => true, 'migrate-after' => true, + 'setup-audit' => true, // 'backup' => see below ], 'source_dir' => str_replace(APPROOT, '', $sSourceDir), diff --git a/tests/php-unit-tests/unitary-tests/setup/sequencers/ApplicationInstallerSequencerTest.php b/tests/php-unit-tests/unitary-tests/setup/sequencers/ApplicationInstallerSequencerTest.php index 654f6a0bc6..b9a7d8728a 100644 --- a/tests/php-unit-tests/unitary-tests/setup/sequencers/ApplicationInstallerSequencerTest.php +++ b/tests/php-unit-tests/unitary-tests/setup/sequencers/ApplicationInstallerSequencerTest.php @@ -62,6 +62,7 @@ class ApplicationInstallerSequencerTest extends ItopTestCase 'optional_steps' => [ 'migrate-before' => true, ], + 'bCallEnterMaintenanceMode' => true, ], ]; } @@ -69,12 +70,12 @@ class ApplicationInstallerSequencerTest extends ItopTestCase /** * @dataProvider FirstStepProvider */ - public function testFirstStep($sNextStep, $sNextLabel, $iPercent, $aOptionalSteps) + public function testFirstStep($sNextStep, $sNextLabel, $iPercent, $aOptionalSteps, bool $bCallEnterMaintenanceMode = false, bool $bCallExitMaintenanceMode = false) { $aAdditionalParams = [ 'optional_steps' => $aOptionalSteps, ]; - $this->GivenApplicationInstallSequencer($aAdditionalParams); + $this->GivenApplicationInstallSequencer($aAdditionalParams, $bCallEnterMaintenanceMode, $bCallExitMaintenanceMode); $aRes = $this->oSequencer->ExecuteStep(); $aExpected = [ @@ -160,7 +161,7 @@ class ApplicationInstallerSequencerTest extends ItopTestCase 'migrate-before' => true, ], ]; - $this->GivenApplicationInstallSequencer($aAdditionalParams); + $this->GivenApplicationInstallSequencer($aAdditionalParams, bCallEnterMaintenanceMode: true); $this->oRunTimeEnvironment->expects($this->once())->method('MigrateDataBeforeUpdateStructure') ->with('install', $this->oConfig); @@ -320,7 +321,7 @@ class ApplicationInstallerSequencerTest extends ItopTestCase public function testCommit() { - $this->GivenApplicationInstallSequencer(); + $this->GivenApplicationInstallSequencer(bCallExitMaintenanceMode: true); $this->oRunTimeEnvironment->expects($this->once())->method('Commit'); $this->oRunTimeEnvironment->expects($this->once())->method('ExitReadOnlyMode'); @@ -484,13 +485,18 @@ class ApplicationInstallerSequencerTest extends ItopTestCase $this->assertEquals(['', 100], $this->oSequencer->GetStepAfterWithPercent('commit')); } - private function GivenRunTimeEnvironment(bool $bStepComputationOnly = false): void + private function GivenRunTimeEnvironment(bool $bStepComputationOnly = false, bool $bCallEnterMaintenanceMode = false, bool $bCallExitMaintenanceMode = false): void { $this->oRunTimeEnvironment = $this->createMock(RunTimeEnvironment::class); if (! $bStepComputationOnly) { $this->oRunTimeEnvironment->expects($this->once())->method('EnterReadOnlyMode') ->with($this->oConfig); } + + $this->oRunTimeEnvironment->expects($this->exactly($bCallEnterMaintenanceMode ? 1 : 0))->method('EnterMaintenanceMode') + ->with($this->oConfig); + + $this->oRunTimeEnvironment->expects($this->exactly($bCallExitMaintenanceMode ? 1 : 0))->method('ExitMaintenanceMode'); } private function GivenConfig(): void @@ -498,10 +504,10 @@ class ApplicationInstallerSequencerTest extends ItopTestCase $this->oConfig = $this->createMock(Config::class); } - private function GivenApplicationInstallSequencer(array $aAdditionalParams = [], bool $bStepComputationOnly = false): void + private function GivenApplicationInstallSequencer(array $aAdditionalParams = [], bool $bStepComputationOnly = false, bool $bCallEnterMaintenanceMode = false, bool $bCallExitMaintenanceMode = false): void { $this->GivenConfig(); - $this->GivenRunTimeEnvironment($bStepComputationOnly); + $this->GivenRunTimeEnvironment($bStepComputationOnly, $bCallEnterMaintenanceMode, $bCallExitMaintenanceMode); $this->oSequencer = new ApplicationInstallSequencer($this->GivenParams($aAdditionalParams), $this->oRunTimeEnvironment); $this->SetNonPublicProperty($this->oSequencer, 'oTestConfig', $this->oConfig); } diff --git a/tests/php-unit-tests/unitary-tests/setup/sequencers/DataAuditSequencerTest.php b/tests/php-unit-tests/unitary-tests/setup/sequencers/DataAuditSequencerTest.php index 33ae70c74f..08721bfcd6 100644 --- a/tests/php-unit-tests/unitary-tests/setup/sequencers/DataAuditSequencerTest.php +++ b/tests/php-unit-tests/unitary-tests/setup/sequencers/DataAuditSequencerTest.php @@ -8,6 +8,8 @@ use PHPParameters; class DataAuditSequencerTest extends ItopTestCase { + private $oConfig; + protected function setUp(): void { static::LoadRequiredItopFiles(); @@ -85,6 +87,7 @@ class DataAuditSequencerTest extends ItopTestCase $aAdditionalParams = [ 'mode' => 'update', + 'optional_steps' => ['setup-audit' => true ], ]; $oSequencer = new DataAuditSequencer($this->GivenParams($aAdditionalParams), $oRunTimeEnvironment); @@ -99,6 +102,30 @@ class DataAuditSequencerTest extends ItopTestCase $this->assertEquals($aExpected, $aRes); } + public function testCompileWithAuditDisabledViaOptionalSteps() + { + $oRunTimeEnvironment = $this->createMock(\RunTimeEnvironment::class); + $oRunTimeEnvironment->expects($this->never())->method('GetApplicationVersion'); + $oRunTimeEnvironment->expects($this->once())->method('DoCompile'); + $oRunTimeEnvironment->expects($this->never())->method('GetFinalEnv') + ->willReturn('production'); + + $aAdditionalParams = [ + 'mode' => 'update', + ]; + $oSequencer = new DataAuditSequencer($this->GivenParams($aAdditionalParams), $oRunTimeEnvironment); + + $aRes = $oSequencer->ExecuteStep('compile'); + $aExpected = [ + 'status' => 1, + 'message' => '', + 'next-step' => 'complete', + 'next-step-label' => 'Check Completed', + 'percentage-completed' => 100, + ]; + $this->assertEquals($aExpected, $aRes); + } + public function testCompileNoAuditInFreshInstall() { $oRunTimeEnvironment = $this->createMock(\RunTimeEnvironment::class); @@ -127,6 +154,7 @@ class DataAuditSequencerTest extends ItopTestCase $aAdditionalParams = [ 'mode' => 'update', + 'optional_steps' => ['setup-audit' => true ], ]; $oSequencer = new DataAuditSequencer($this->GivenParams($aAdditionalParams), $oRunTimeEnvironment); @@ -171,6 +199,7 @@ class DataAuditSequencerTest extends ItopTestCase $aAdditionalParams = [ 'mode' => 'update', + 'optional_steps' => ['setup-audit' => true ], ]; $oSequencer = new DataAuditSequencer($this->GivenParams($aAdditionalParams), $oRunTimeEnvironment); @@ -196,6 +225,7 @@ class DataAuditSequencerTest extends ItopTestCase $aAdditionalParams = [ 'mode' => 'update', + 'optional_steps' => ['setup-audit' => true ], ]; $oSequencer = new DataAuditSequencer($this->GivenParams($aAdditionalParams), $oRunTimeEnvironment); $aRes = $oSequencer->ExecuteStep('setup-audit'); @@ -272,7 +302,11 @@ class DataAuditSequencerTest extends ItopTestCase ->willReturn(['product_version' => ITOP_VERSION_FULL."_alpha"]); $oRunTimeEnvironment->expects($this->never())->method("GetFinalEnv"); - $oSequencer = new DataAuditSequencer($this->GivenParams(['mode' => 'upgrade']), $oRunTimeEnvironment); + $aAdditionalParams = [ + 'mode' => 'upgrade', + 'optional_steps' => ['setup-audit' => true ], + ]; + $oSequencer = new DataAuditSequencer($this->GivenParams($aAdditionalParams), $oRunTimeEnvironment); self::assertFalse($this->InvokeNonPublicMethod(DataAuditSequencer::class, "IsDataAuditRequired", $oSequencer)); } @@ -283,7 +317,25 @@ class DataAuditSequencerTest extends ItopTestCase ->willReturn(['product_version' => ITOP_VERSION_FULL]); $oRunTimeEnvironment->expects($this->once())->method("GetFinalEnv")->willReturn("production-gabuzomeu"); - $oSequencer = new DataAuditSequencer($this->GivenParams(['mode' => 'upgrade']), $oRunTimeEnvironment); + $aAdditionalParams = [ + 'mode' => 'upgrade', + 'optional_steps' => ['setup-audit' => true ], + ]; + $oSequencer = new DataAuditSequencer($this->GivenParams($aAdditionalParams), $oRunTimeEnvironment); + self::assertFalse($this->InvokeNonPublicMethod(DataAuditSequencer::class, "IsDataAuditRequired", $oSequencer)); + } + + public function testIsDataAuditRequired_NoAuditTriggeredBecauseDisabled() + { + $oRunTimeEnvironment = $this->createMock(\RunTimeEnvironment::class); + $oRunTimeEnvironment->expects($this->once())->method('GetApplicationVersion') + ->willReturn(['product_version' => ITOP_VERSION_FULL]); + $oRunTimeEnvironment->expects($this->once())->method("GetFinalEnv")->willReturn("production"); + + $aAdditionalParams = [ + 'mode' => 'upgrade', + ]; + $oSequencer = new DataAuditSequencer($this->GivenParams($aAdditionalParams), $oRunTimeEnvironment); self::assertFalse($this->InvokeNonPublicMethod(DataAuditSequencer::class, "IsDataAuditRequired", $oSequencer)); } @@ -294,7 +346,11 @@ class DataAuditSequencerTest extends ItopTestCase ->willReturn(['product_version' => ITOP_VERSION_FULL]); $oRunTimeEnvironment->expects($this->once())->method("GetFinalEnv")->willReturn("production"); - $oSequencer = new DataAuditSequencer($this->GivenParams(['mode' => 'upgrade']), $oRunTimeEnvironment); + $aAdditionalParams = [ + 'mode' => 'upgrade', + 'optional_steps' => ['setup-audit' => true ], + ]; + $oSequencer = new DataAuditSequencer($this->GivenParams($aAdditionalParams), $oRunTimeEnvironment); self::assertTrue($this->InvokeNonPublicMethod(DataAuditSequencer::class, "IsDataAuditRequired", $oSequencer)); } } From f4a1aca3f98e5b7155736fe58eb286ac7fb33827 Mon Sep 17 00:00:00 2001 From: Eric Espie Date: Wed, 6 May 2026 11:54:45 +0200 Subject: [PATCH 5/6] Changed display of modules and installation choices --- setup/unattended-install/unattended-install.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/setup/unattended-install/unattended-install.php b/setup/unattended-install/unattended-install.php index c1d9e63b13..b6362bd723 100644 --- a/setup/unattended-install/unattended-install.php +++ b/setup/unattended-install/unattended-install.php @@ -82,7 +82,7 @@ if (! is_null($sInstallationXmlPath) && is_file($sInstallationXmlPath)) { $aComputedExtensions = $oInstallationFileService->GetAfterComputationSelectedExtensions(); if (! $bInstallationChoicesProvided) { $sMsg = "Computed installation choices"; - echo "$sMsg:\n".implode(',', $aComputedExtensions)."\n\n"; + echo "$sMsg:\n".implode(",\n", $aComputedExtensions)."\n\n"; SetupLog::Info($sMsg, null, $aComputedExtensions); $oParams->Set('selected_extensions', $aComputedExtensions); } @@ -97,8 +97,8 @@ if (! is_null($sInstallationXmlPath) && is_file($sInstallationXmlPath)) { $sMsg = "Modules to install listed in $sXmlSetupBaseName (selected_modules section)"; } -sort($aSelectedModules); -echo "$sMsg:\n".implode(',', $aSelectedModules)."\n\n"; +//sort($aSelectedModules); +echo "$sMsg:\n".implode(",\n", $aSelectedModules)."\n\n"; SetupLog::Info($sMsg, null, $aSelectedModules); // Configuration file From ae1f9391f95cce55abab50c1ae0b9ecf2b31fbf0 Mon Sep 17 00:00:00 2001 From: odain Date: Wed, 6 May 2026 13:22:46 +0200 Subject: [PATCH 6/6] =?UTF-8?q?N=C2=B09144=20-=20PR=20review=20feedbacks?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- setup/runtimeenv.class.inc.php | 4 +++- .../setup/sequencers/DataAuditSequencerTest.php | 8 ++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/setup/runtimeenv.class.inc.php b/setup/runtimeenv.class.inc.php index 913c58ee80..f6a80b69b7 100644 --- a/setup/runtimeenv.class.inc.php +++ b/setup/runtimeenv.class.inc.php @@ -1566,7 +1566,9 @@ class RunTimeEnvironment public function ExitMaintenanceMode(): void { - SetupUtils::ExitMaintenanceMode(); + if (SetupUtils::IsInMaintenanceMode()){ + SetupUtils::ExitMaintenanceMode(); + } } public function ExitReadOnlyMode() diff --git a/tests/php-unit-tests/unitary-tests/setup/sequencers/DataAuditSequencerTest.php b/tests/php-unit-tests/unitary-tests/setup/sequencers/DataAuditSequencerTest.php index 08721bfcd6..1313d64402 100644 --- a/tests/php-unit-tests/unitary-tests/setup/sequencers/DataAuditSequencerTest.php +++ b/tests/php-unit-tests/unitary-tests/setup/sequencers/DataAuditSequencerTest.php @@ -120,8 +120,8 @@ class DataAuditSequencerTest extends ItopTestCase 'status' => 1, 'message' => '', 'next-step' => 'complete', - 'next-step-label' => 'Check Completed', - 'percentage-completed' => 100, + 'next-step-label' => 'Check Completed', + 'percentage-completed' => 100, ]; $this->assertEquals($aExpected, $aRes); } @@ -328,9 +328,9 @@ class DataAuditSequencerTest extends ItopTestCase public function testIsDataAuditRequired_NoAuditTriggeredBecauseDisabled() { $oRunTimeEnvironment = $this->createMock(\RunTimeEnvironment::class); - $oRunTimeEnvironment->expects($this->once())->method('GetApplicationVersion') + $oRunTimeEnvironment->expects($this->never())->method('GetApplicationVersion') ->willReturn(['product_version' => ITOP_VERSION_FULL]); - $oRunTimeEnvironment->expects($this->once())->method("GetFinalEnv")->willReturn("production"); + $oRunTimeEnvironment->expects($this->never())->method("GetFinalEnv")->willReturn("production"); $aAdditionalParams = [ 'mode' => 'upgrade',