N°9711 - Block user from going back to the setup (#954)

* N°9711 - Store setup parameters in session

* N°9711 - Restrict database parameters display to administrators in setup summary
This commit is contained in:
Eric Espié
2026-07-02 12:00:22 +02:00
committed by GitHub
parent e882df96d9
commit a335e1004b
21 changed files with 311 additions and 188 deletions

View File

@@ -614,6 +614,13 @@ class LogChannels
* @since 3.2.0
*/
public const SECURITY = 'Security';
/**
* For Session parameters
*
* @Since 3.3.0
*/
public const SESSION_PARAMETERS = 'SessionParameters';
}
abstract class LogAPI

View File

@@ -19,6 +19,7 @@ use Combodo\iTop\DataFeatureRemoval\Helper\DataFeatureRemovalLog;
use Combodo\iTop\DataFeatureRemoval\Service\DataCleanupService;
use Combodo\iTop\DataFeatureRemoval\Service\DataFeatureRemoverExtensionService;
use Combodo\iTop\DataFeatureRemoval\Service\StaticDeletionPlan;
use Combodo\iTop\Service\Session\SessionParameters;
use Combodo\iTop\Setup\FeatureRemoval\DryRemovalRuntimeEnvironment;
use Combodo\iTop\Setup\FeatureRemoval\SetupAudit;
use ContextTag;
@@ -47,6 +48,9 @@ class DataFeatureRemovalController extends Controller
{
$aParams = [];
SetupUtils::EraseSetupToken();
(new SessionParameters(SetupUtils::SESSION_PARAMETERS_NAME))->Erase();
$this->AddAnalyzeParams();
$aParams['sTransactionId'] = utils::GetNewTransactionId();
$aParams['iColumnCount'] = $this->iColumnCount;
@@ -180,7 +184,7 @@ class DataFeatureRemovalController extends Controller
];
foreach ($aHiddenInputs as $sInputName => $sInputValue) {
$aParams['aSetupParams']["_params[$sInputName]"] = $sInputValue;
$aParams['aSetupParams'][$sInputName] = $sInputValue;
}
[$aParams['aDeletionPlanSummary'], $aParams['iQueryCount'], $aParams['bDeletionPossible']] = $this->GetDeletionPlanSummaryTable($aGetRemovedClasses);
@@ -189,6 +193,7 @@ class DataFeatureRemovalController extends Controller
Session::Set('aDeletionExecutionSummary', serialize($this->aDeletionExecutionSummary));
if (!$aParams['bDeletionNeeded']) {
// Erase session setup parameters
SetupUtils::CreateSetupToken();
}
@@ -206,12 +211,13 @@ class DataFeatureRemovalController extends Controller
}
/**
* @param array $aAddedExtensions
* @param array $aRemovedExtensions
* @param bool $bForceCompilation
* @return void
* @throws \ConfigException
* @throws \CoreException
* @param array $aAddedExtensions
* @param array $aRemovedExtensions
* @param bool $bForceCompilation
*
* @return void
* @throws \ConfigException
* @throws \CoreException
*/
private function Compile(array $aAddedExtensions, array $aRemovedExtensions, bool $bForceCompilation = true): void
{

View File

@@ -14,10 +14,7 @@ if (PHP_VERSION_ID < 50600) {
echo $err;
}
}
trigger_error(
$err,
E_USER_ERROR
);
throw new RuntimeException($err);
}
require_once __DIR__ . '/composer/autoload_real.php';

View File

@@ -137,7 +137,6 @@ return array(
'Combodo\\iTop\\Application\\Helper\\FormHelper' => $baseDir . '/sources/Application/Helper/FormHelper.php',
'Combodo\\iTop\\Application\\Helper\\ImportHelper' => $baseDir . '/sources/Application/Helper/ImportHelper.php',
'Combodo\\iTop\\Application\\Helper\\SearchHelper' => $baseDir . '/sources/Application/Helper/SearchHelper.php',
'Combodo\\iTop\\Application\\Helper\\Session' => $baseDir . '/sources/Application/Helper/Session.php',
'Combodo\\iTop\\Application\\Helper\\SynchroReplicaHelper' => $baseDir . '/sources/Application/Helper/SynchroReplicaHelper.php',
'Combodo\\iTop\\Application\\Helper\\WebResourcesHelper' => $baseDir . '/sources/Application/Helper/WebResourcesHelper.php',
'Combodo\\iTop\\Application\\Newsroom\\iTopNewsroomProvider' => $baseDir . '/sources/Application/Newsroom/iTopNewsroomProvider.php',
@@ -640,6 +639,8 @@ return array(
'Combodo\\iTop\\Service\\Router\\Exception\\RouteNotFoundException' => $baseDir . '/sources/Service/Router/Exception/RouteNotFoundException.php',
'Combodo\\iTop\\Service\\Router\\Exception\\RouterException' => $baseDir . '/sources/Service/Router/Exception/RouterException.php',
'Combodo\\iTop\\Service\\Router\\Router' => $baseDir . '/sources/Service/Router/Router.php',
'Combodo\\iTop\\Service\\Session\\Session' => $baseDir . '/sources/Service/Session/Session.php',
'Combodo\\iTop\\Service\\Session\\SessionParameters' => $baseDir . '/sources/Service/Session/SessionParameters.php',
'Combodo\\iTop\\Service\\Startup\\StartupService' => $baseDir . '/sources/Service/Startup/StartupService.php',
'Combodo\\iTop\\Service\\SummaryCard\\SummaryCardService' => $baseDir . '/sources/Service/SummaryCard/SummaryCardService.php',
'Combodo\\iTop\\Service\\TemporaryObjects\\TemporaryObjectConfig' => $baseDir . '/sources/Service/TemporaryObjects/TemporaryObjectConfig.php',
@@ -1423,6 +1424,7 @@ return array(
'ReportValue' => $baseDir . '/core/bulkchange.class.inc.php',
'RestDelete' => $baseDir . '/core/restservices.class.inc.php',
'RestResult' => $baseDir . '/application/applicationextension/rest/RestResult.php',
'RestResultWithObjectSets' => $baseDir . '/core/restservices.class.inc.php',
'RestResultWithObjects' => $baseDir . '/core/restservices.class.inc.php',
'RestResultWithRelations' => $baseDir . '/core/restservices.class.inc.php',
'RestUtils' => $baseDir . '/application/applicationextension/rest/RestUtils.php',

View File

@@ -538,7 +538,6 @@ class ComposerStaticInitfc0e9e9dea11dcbb6272414776c30685
'Combodo\\iTop\\Application\\Helper\\FormHelper' => __DIR__ . '/../..' . '/sources/Application/Helper/FormHelper.php',
'Combodo\\iTop\\Application\\Helper\\ImportHelper' => __DIR__ . '/../..' . '/sources/Application/Helper/ImportHelper.php',
'Combodo\\iTop\\Application\\Helper\\SearchHelper' => __DIR__ . '/../..' . '/sources/Application/Helper/SearchHelper.php',
'Combodo\\iTop\\Application\\Helper\\Session' => __DIR__ . '/../..' . '/sources/Application/Helper/Session.php',
'Combodo\\iTop\\Application\\Helper\\SynchroReplicaHelper' => __DIR__ . '/../..' . '/sources/Application/Helper/SynchroReplicaHelper.php',
'Combodo\\iTop\\Application\\Helper\\WebResourcesHelper' => __DIR__ . '/../..' . '/sources/Application/Helper/WebResourcesHelper.php',
'Combodo\\iTop\\Application\\Newsroom\\iTopNewsroomProvider' => __DIR__ . '/../..' . '/sources/Application/Newsroom/iTopNewsroomProvider.php',
@@ -1041,6 +1040,8 @@ class ComposerStaticInitfc0e9e9dea11dcbb6272414776c30685
'Combodo\\iTop\\Service\\Router\\Exception\\RouteNotFoundException' => __DIR__ . '/../..' . '/sources/Service/Router/Exception/RouteNotFoundException.php',
'Combodo\\iTop\\Service\\Router\\Exception\\RouterException' => __DIR__ . '/../..' . '/sources/Service/Router/Exception/RouterException.php',
'Combodo\\iTop\\Service\\Router\\Router' => __DIR__ . '/../..' . '/sources/Service/Router/Router.php',
'Combodo\\iTop\\Service\\Session\\Session' => __DIR__ . '/../..' . '/sources/Service/Session/Session.php',
'Combodo\\iTop\\Service\\Session\\SessionParameters' => __DIR__ . '/../..' . '/sources/Service/Session/SessionParameters.php',
'Combodo\\iTop\\Service\\Startup\\StartupService' => __DIR__ . '/../..' . '/sources/Service/Startup/StartupService.php',
'Combodo\\iTop\\Service\\SummaryCard\\SummaryCardService' => __DIR__ . '/../..' . '/sources/Service/SummaryCard/SummaryCardService.php',
'Combodo\\iTop\\Service\\TemporaryObjects\\TemporaryObjectConfig' => __DIR__ . '/../..' . '/sources/Service/TemporaryObjects/TemporaryObjectConfig.php',
@@ -1824,6 +1825,7 @@ class ComposerStaticInitfc0e9e9dea11dcbb6272414776c30685
'ReportValue' => __DIR__ . '/../..' . '/core/bulkchange.class.inc.php',
'RestDelete' => __DIR__ . '/../..' . '/core/restservices.class.inc.php',
'RestResult' => __DIR__ . '/../..' . '/application/applicationextension/rest/RestResult.php',
'RestResultWithObjectSets' => __DIR__ . '/../..' . '/core/restservices.class.inc.php',
'RestResultWithObjects' => __DIR__ . '/../..' . '/core/restservices.class.inc.php',
'RestResultWithRelations' => __DIR__ . '/../..' . '/core/restservices.class.inc.php',
'RestUtils' => __DIR__ . '/../..' . '/application/applicationextension/rest/RestUtils.php',

View File

@@ -36,8 +36,7 @@ if ($issues) {
echo 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . str_replace('You are running '.PHP_VERSION.'.', '', implode(PHP_EOL, $issues)) . PHP_EOL.PHP_EOL;
}
}
trigger_error(
'Composer detected issues in your platform: ' . implode(' ', $issues),
E_USER_ERROR
throw new \RuntimeException(
'Composer detected issues in your platform: ' . implode(' ', $issues)
);
}

View File

@@ -103,6 +103,9 @@ class CheckResult
*/
class SetupUtils
{
// Name of the parameter array in session for setup
public const SESSION_PARAMETERS_NAME = 'setup_params';
// -- Minimum versions (requirements : forbids installation if not met)
public const PHP_MIN_VERSION = '8.2.0';
public const MYSQL_MIN_VERSION = '5.7.0'; // 5.6 is no longer supported

View File

@@ -1,21 +1,11 @@
<?php
// Copyright (C) 2010-2024 Combodo SAS
//
// This file is part of iTop.
//
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// iTop is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
/**
* @copyright Copyright (C) 2010-2026 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0
*/
use Combodo\iTop\Service\Session\SessionParameters;
require_once(APPROOT.'setup/setuputils.class.inc.php');
require_once(APPROOT.'setup/parameters.class.inc.php');
@@ -26,14 +16,12 @@ require_once(APPROOT.'setup/extensionsmap.class.inc.php');
/**
* Engine for displaying the various pages of a "wizard"
* Each "step" of the wizard must be implemented as
* separate class derived from WizardStep. each 'step' can also have its own
* separate classes derived from WizardStep. Each 'step' can also have its own
* internal 'state' for developing complex wizards.
* The WizardController provides the "<< Back" feature by storing a stack
* of the previous screens. The WizardController also maintains from page
* to page a list of "parameters" to be dispayed/edited by each of the steps.
*
* @copyright Copyright (C) 2010-2024 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0
*/
class WizardController
@@ -41,37 +29,42 @@ class WizardController
protected $aWizardSteps;
protected $sInitialStepClass;
protected $sInitialState;
protected $aParameters;
protected SessionParameters $oSessionParameters;
/**
* Initiailization of the wizard controller
* Initialization of the wizard controller
* @param string $sInitialStepClass Class of the initial step/page of the wizard
* @param string $sInitialState Initial state of the initial page (if this class manages states)
*/
public function __construct($sInitialStepClass, $sInitialState = '')
public function __construct(string $sInitialStepClass, string $sInitialState = '')
{
$this->sInitialStepClass = $sInitialStepClass;
$this->sInitialState = $sInitialState;
$this->aParameters = [];
$this->aWizardSteps = [];
$this->oSessionParameters = new SessionParameters(SetupUtils::SESSION_PARAMETERS_NAME);
$this->oSessionParameters->LogParameters();
$this->aWizardSteps = $this->GetParameter('_steps', []);
}
/**
* Pushes information about the current step onto the stack
* @param array $aStepInfo Array('class' => , 'state' => )
*/
protected function PushStep($aStepInfo)
protected function PushStep(array $aStepInfo): void
{
array_push($this->aWizardSteps, $aStepInfo);
$this->aWizardSteps[] = $aStepInfo;
$this->SetParameter('_steps', $this->aWizardSteps);
}
/**
* Removes information about the previous step from the stack
* @return array{'class': string, 'state': string}
*/
protected function PopStep()
protected function PopStep(): array
{
return array_pop($this->aWizardSteps);
$aStep = array_pop($this->aWizardSteps);
$this->SetParameter('_steps', $this->aWizardSteps);
return $aStep;
}
/**
@@ -79,13 +72,9 @@ class WizardController
* @param string $sParamCode The code identifying this parameter
* @param mixed $defaultValue The default value of the parameter in case it was not set
*/
public function GetParameter($sParamCode, $defaultValue = '')
public function GetParameter(string $sParamCode, mixed $defaultValue = ''): mixed
{
if (array_key_exists($sParamCode, $this->aParameters)) {
return $this->aParameters[$sParamCode];
}
return $defaultValue;
return $this->oSessionParameters->GetParameter($sParamCode, $defaultValue);
}
/**
@@ -115,9 +104,9 @@ class WizardController
* @param string $sParamCode The code identifying this parameter
* @param mixed $value The value to store
*/
public function SetParameter($sParamCode, $value)
public function SetParameter(string $sParamCode, mixed $value): void
{
$this->aParameters[$sParamCode] = $value;
$this->oSessionParameters->SetParameter($sParamCode, $value);
}
/**
@@ -126,30 +115,46 @@ class WizardController
* @param mixed $defaultValue The default value for the parameter
* @param string $sSanitizationFilter A 'sanitization' fitler. Default is 'raw_data', which means no filtering
*/
public function SaveParameter($sParamCode, $defaultValue, $sSanitizationFilter = 'raw_data')
public function SaveParameter(string $sParamCode, mixed $defaultValue, string $sSanitizationFilter = 'raw_data'): void
{
$value = utils::ReadParam($sParamCode, $defaultValue, false, $sSanitizationFilter);
$this->aParameters[$sParamCode] = $value;
$this->oSessionParameters->SetParameterFromParams($sParamCode, $defaultValue, $sSanitizationFilter);
}
/**
* Stores the value of the page's parameter in a "persistent" parameter in the wizard's context
* @param string $sParamCode The code identifying this parameter
* @param mixed $defaultValue The default value for the parameter
* @param string $sSanitizationFilter A 'sanitization' fitler. Default is 'raw_data', which means no filtering
*/
public function SavePostedParameter(string $sParamCode, mixed $defaultValue = '', string $sSanitizationFilter = 'raw_data'): void
{
$this->oSessionParameters->SetParameterFromPostedParams($sParamCode, $defaultValue, $sSanitizationFilter);
}
/**
* Starts the wizard by displaying it in its initial state
*
* @throws \Exception
*/
public function Start()
public function Start(): void
{
if ($this->GetParameter('return_application', '') === '') {
// Fresh restart of the wizard
$this->EraseParameters();
}
$sCurrentStepClass = $this->sInitialStepClass;
$oStep = $this->GetWizardStep($sCurrentStepClass, $this->sInitialState);
$oStep = $this->InstantiateWizardStep($sCurrentStepClass, $this->sInitialState);
$this->DisplayStep($oStep);
}
/**
* Progress towards the next step of the wizard
* @throws Exception
*/
protected function Next()
protected function Next(): void
{
$sCurrentStepClass = utils::ReadParam('_class', $this->sInitialStepClass);
$sCurrentState = utils::ReadParam('_state', $this->sInitialState);
$oStep = $this->GetWizardStep($sCurrentStepClass, $sCurrentState);
$oStep = $this->InstantiateWizardStep($sCurrentStepClass, $sCurrentState);
if ($oStep->ValidateParams()) {
$aPossibleSteps = $oStep->GetPossibleSteps();
if ($oStep->CanMoveBackward()) {
@@ -157,7 +162,7 @@ class WizardController
}
$oWizardState = $oStep->UpdateWizardStateAndGetNextStep(true); // true => moving forward
if (in_array($oWizardState->GetNextStep(), $aPossibleSteps)) {
$oNextStep = $this->GetWizardStep($oWizardState->GetNextStep(), $oWizardState->GetState());
$oNextStep = $this->InstantiateWizardStep($oWizardState->GetNextStep(), $oWizardState->GetState());
$this->DisplayStep($oNextStep);
} else {
throw new Exception("Internal error: Unexpected next step '{$oWizardState->GetNextStep()}'. The possible next steps are: ".implode(', ', $aPossibleSteps));
@@ -169,18 +174,20 @@ class WizardController
/**
* Move one step back
*
* @throws \Exception
*/
protected function Back()
protected function Back(): void
{
// let the current step save its parameters
$sCurrentStepClass = utils::ReadParam('_class', $this->sInitialStepClass);
$sCurrentState = utils::ReadParam('_state', $this->sInitialState);
$oStep = $this->GetWizardStep($sCurrentStepClass, $sCurrentState);
$oStep = $this->InstantiateWizardStep($sCurrentStepClass, $sCurrentState);
$oStep->UpdateWizardStateAndGetNextStep(false); // false => Moving backwards
// Display the previous step
$aCurrentStepInfo = $this->PopStep();
$oStep = $this->GetWizardStep($aCurrentStepInfo['class'], $aCurrentStepInfo['state']);
$oStep = $this->InstantiateWizardStep($aCurrentStepInfo['class'], $aCurrentStepInfo['state']);
$this->DisplayStep($oStep);
}
@@ -195,6 +202,24 @@ class WizardController
{
SetupLog::Info("=== Setup screen: ".$oStep->GetTitle().' ('.get_class($oStep).')');
$oPage = new SetupPage($oStep->GetTitle());
if (!$oStep->CanAccessToWizardStep()) {
[$sButtonLabel, $sButtonUrl] = SetupUtils::GetBackButtonInfo($this->oSessionParameters->GetParameter('return_application', ''));
SetupUtils::ExitReadOnlyMode(false); // Reset readonly mode in case of problem
SetupUtils::EraseSetupToken();
$this->oSessionParameters->Erase();
$oP = new SetupPage('Installation Cannot Continue');
$oP->add("<h2>Fatal error</h2>\n");
$oP->error("<b>Error:</b> This setup step is not accessible with your access rights.");
$sButtonsHtml = <<<HTML
<button type="button" class="ibo-button ibo-is-regular ibo-is-primary" onclick="window.location.href='$sButtonUrl'">$sButtonLabel</button>
HTML;
$oP->p($sButtonsHtml);
$oP->output();
// Prevent token creation
exit;
}
$oPage->LinkScriptFromAppRoot('setup/setup.js');
$oPage->add('<form id="wiz_form" class="ibo-setup--wizard" method="post">');
@@ -208,11 +233,6 @@ class WizardController
// to store the parameters
$oPage->add('<input type="hidden" id="_class" name="_class" value="'.get_class($oStep).'"/>');
$oPage->add('<input type="hidden" id="_state" name="_state" value="'.$oStep->GetState().'"/>');
foreach ($this->aParameters as $sCode => $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->aWizardSteps)).'"/>');
$oPage->add('<table style="width:100%;" class="ibo-setup--wizard--buttons-container"><tr>');
if (count($this->aWizardSteps) > 0) {
if ($oStep->CanMoveBackward()) {
@@ -267,7 +287,7 @@ EOF
* Make the wizard run: 'Start', 'Next' or 'Back' depending WizardUpdateButtons();
* on the page's parameters
*/
public function Run()
public function Run(): void
{
/**
* @since 3.2.0 Add the ContextTag init
@@ -276,8 +296,6 @@ EOF
$oContextTag = new ContextTag(ContextTag::TAG_SETUP);
$sOperation = utils::ReadParam('operation');
$this->aParameters = utils::ReadParam('_params', [], false, 'raw_data');
$this->SetWizardSteps(json_decode(utils::ReadParam('_steps', '[]', false, 'raw_data'), true));
switch ($sOperation) {
case 'next':
@@ -293,65 +311,6 @@ EOF
}
}
/**
* Provides information about the structure/workflow of the wizard by listing
* the possible list of 'steps' and their dependencies
* @param string $sStep Name of the class to start from (used for recursion)
* @param array $aAllSteps List of steps (used for recursion)
*/
public function DumpStructure($sStep = '', $aAllSteps = null)
{
if ($aAllSteps == null) {
$aAllSteps = [];
}
if ($sStep == '') {
$sStep = $this->sInitialStepClass;
}
$oStep = $this->GetWizardStep($sStep);
$aAllSteps[$sStep] = $oStep->GetPossibleSteps();
foreach ($aAllSteps[$sStep] as $sNextStep) {
if (!array_key_exists($sNextStep, $aAllSteps)) {
$aAllSteps = $this->DumpStructure($sNextStep, $aAllSteps);
}
}
return $aAllSteps;
}
/**
* Dump the wizard's structure as a string suitable to produce a chart
* using graphviz's "dot" program
* @return string The 'dot' formatted output
*/
public function DumpStructureAsDot()
{
$aAllSteps = $this->DumpStructure();
$sOutput = "digraph finite_state_machine {\n";
//$sOutput .= "\trankdir=LR;";
$sOutput .= "\tsize=\"10,12\"\n";
$aDeadEnds = [$this->sInitialStepClass];
foreach ($aAllSteps as $sStep => $aNextSteps) {
if (count($aNextSteps) == 0) {
$aDeadEnds[] = $sStep;
}
}
$sOutput .= "\tnode [shape = doublecircle]; ".implode(' ', $aDeadEnds).";\n";
$sOutput .= "\tnode [shape = box];\n";
foreach ($aAllSteps as $sStep => $aNextSteps) {
$oStep = $this->GetWizardStep($sStep);
$sOutput .= "\t$sStep [ label = \"".$oStep->GetTitle()."\"];\n";
if (count($aNextSteps) > 0) {
foreach ($aNextSteps as $sNextStep) {
$sOutput .= "\t$sStep -> $sNextStep;\n";
}
}
}
$sOutput .= "}\n";
return $sOutput;
}
public function SetWizardSteps(array $aWizardSteps): void
{
$this->aWizardSteps = $aWizardSteps;
@@ -364,11 +323,16 @@ EOF
* @return \WizardStep
* @throws \Exception
*/
private function GetWizardStep(string $sCurrentStepClass, string $sCurrentState = ''): WizardStep
private function InstantiateWizardStep(string $sCurrentStepClass, string $sCurrentState = ''): WizardStep
{
if (!is_subclass_of($sCurrentStepClass, WizardStep::class)) {
throw new Exception('Unknown step '.$sCurrentStepClass);
}
return new $sCurrentStepClass($this, $sCurrentState);
}
public function EraseParameters()
{
$this->oSessionParameters->Erase();
}
}

View File

@@ -55,9 +55,6 @@ abstract class AbstractWizStepInstall extends WizardStep
$sSourceDir = $this->oWizard->GetParameter('source_dir');
if (($sMode == 'upgrade') && ($this->oWizard->GetParameter('upgrade_type') == 'keep-previous')) {
//$sPreviousVersionDir = $this->oWizard->GetParameter('previous_version_dir');
//$aCopies[] = ['source' => $sSourceDir, 'destination' => 'modules']; // Source is an absolute path, destination is relative to APPROOT
//$aCopies[] = ['source' => $sPreviousVersionDir.'/portal', 'destination' => 'portal']; // Source is an absolute path, destination is relative to APPROOT
$sSourceDir = APPROOT.'modules';
}

View File

@@ -43,6 +43,11 @@ class WizStepDataAudit extends WizStepInstall
return 'Next';
}
public function CanAccessToWizardStep()
{
return true;
}
public function CanMoveForward()
{
if ($this->CheckDependencies()) {
@@ -135,7 +140,7 @@ HTML
$sButtonUrl = utils::HtmlEntities($sButtonUrl);
$oPage->add_ready_script(
<<<JS
$('.ibo-setup--wizard--buttons-container tr td:nth-child(1)').after('<td style="text-align:center;"><button id="return-button" class="ibo-button ibo-is-alternative ibo-is-neutral ibo-is-hidden" type="button" onclick="window.location.href=\'$sButtonUrl\'"><span class="ibo-button--label">$sButtonLabel</span></button></td>');
$('.ibo-setup--wizard--buttons-container tr td:nth-child(1)').before('<td style="text-align:center;"><button id="return-button" class="ibo-button ibo-is-alternative ibo-is-neutral ibo-is-hidden" type="button" onclick="window.location.href=\'$sButtonUrl\'"><span class="ibo-button--label">$sButtonLabel</span></button></td>');
JS
);
}

View File

@@ -132,9 +132,15 @@ class WizStepDone extends WizardStep
if (false === $bHasBackup) {
SetupUtils::EraseSetupToken();
$this->oWizard->EraseParameters();
}
}
public function CanAccessToWizardStep()
{
return true;
}
public function CanMoveForward()
{
return false;
@@ -152,6 +158,7 @@ class WizStepDone extends WizardStep
public function AsyncAction(WebPage $oPage, $sCode, $aParameters)
{
SetupUtils::EraseSetupToken();
$this->oWizard->EraseParameters();
// For security reasons: add the extension now so that this action can be used to read *only* .tar.gz files from the disk...
$sBackupFile = $aParameters['backup'].'.tar.gz';
if (file_exists($sBackupFile)) {

View File

@@ -45,6 +45,11 @@ class WizStepInstall extends AbstractWizStepInstall
return 'Continue';
}
public function CanAccessToWizardStep()
{
return true;
}
public function CanMoveForward()
{
if ($this->CheckDependencies()) {
@@ -120,7 +125,7 @@ JS);
$sButtonUrl = utils::HtmlEntities($sButtonUrl);
$oPage->add_ready_script(
<<<JS
$('.ibo-setup--wizard--buttons-container tr td:nth-child(1)').after('<td style="text-align:center;"><button id="return-button" class="ibo-button ibo-is-alternative ibo-is-neutral ibo-is-hidden" type="button" onclick="window.location.href=\'$sButtonUrl\'"><span class="ibo-button--label">$sButtonLabel</span></button></td>');
$('.ibo-setup--wizard--buttons-container tr td:nth-child(1)').before('<td style="text-align:center;"><button id="return-button" class="ibo-button ibo-is-alternative ibo-is-neutral ibo-is-hidden" type="button" onclick="window.location.href=\'$sButtonUrl\'"><span class="ibo-button--label">$sButtonLabel</span></button></td>');
JS
);
}

View File

@@ -34,7 +34,8 @@ class WizStepLandingBeforeAudit extends WizStepModulesChoice
$oWizard->SetParameter('extensions_not_uninstallable', '[]');
$oWizard->SaveParameter('use_symbolic_links', MFCompiler::UseSymbolicLinks());
$oWizard->SaveParameter('force-uninstall', '');
$oWizard->SaveParameter('force-uninstall', false);
$oWizard->SaveParameter('skip_wizard', false);
// should be done at the end
parent::__construct($oWizard, $sCurrentState, false);
@@ -52,7 +53,8 @@ class WizStepLandingBeforeAudit extends WizStepModulesChoice
*/
public function UpdateWizardStateAndGetNextStep($bMoveForward = true): WizardState
{
if ($this->oWizard->GetParameter('skip_wizard', false)) {
$bSkipWizard = $this->oWizard->GetParameter('skip_wizard', false);
if ($bSkipWizard) {
$oRuntimeEnv = new RunTimeEnvironment();
$sBuildConfigFile = APPCONF.$oRuntimeEnv->GetBuildEnv().'/'.ITOP_CONFIG_FILE;
$oConfig = new Config($sBuildConfigFile);
@@ -61,24 +63,45 @@ class WizStepLandingBeforeAudit extends WizStepModulesChoice
$this->oWizard->SetParameter('selected_extensions', json_encode($aExtensionsFromDatabase));
$adModulesFromDatabase = ModuleInstallationRepository::GetInstance()->ReadComputeInstalledModules($oConfig);
$this->oWizard->SetParameter('selected_modules', json_encode(array_keys($adModulesFromDatabase)));
} else {
$this->oWizard->SavePostedParameter('selected_modules');
$this->oWizard->SavePostedParameter('selected_extensions');
$this->oWizard->SavePostedParameter('added_extensions');
$this->oWizard->SavePostedParameter('removed_extensions');
$this->oWizard->SavePostedParameter('extensions_not_uninstallable');
$this->oWizard->SavePostedParameter('copy_setup_files', '1');
$this->oWizard->SavePostedParameter('force-uninstall');
$this->oWizard->SavePostedParameter('use_symbolic_links');
$this->oWizard->SavePostedParameter('return_application');
$this->oWizard->SavePostedParameter('target_env');
}
$aWizardSteps = $this->GetWizardSteps();
$this->oWizard->SetWizardSteps($aWizardSteps);
$this->sCurrentState = count($aWizardSteps) - 1;
$aWizardSteps = $this->oWizard->GetParameter('_steps', null);
if (is_null($aWizardSteps)) {
$aWizardSteps = $this->GetWizardSteps();
if ($bSkipWizard) {
$this->oWizard->SetParameter('_steps', $aWizardSteps);
}
$aSelectedComponents = $this->GetSelectedComponents($this->aSteps, $this->oWizard->GetParameter('selected_extensions'));
$this->oWizard->SetParameter('selected_components', json_encode($aSelectedComponents));
// Component selection in previous screens
if ($this->oWizard->GetParameter('selected_components', '[]') === '[]') {
// Save the choices for the summary step
$sDisplayChoices = '<ul>';
$i = 0;
foreach ($this->aSteps as $aStepInfo) {
$sDisplayChoices .= $this->GetSelectedModules($aStepInfo, $aSelectedComponents[$i], $aModules, '', '', $aExtensions);
$i++;
$aSelectedComponents = $this->GetSelectedComponents($this->aSteps, $this->oWizard->GetParameter('selected_extensions', '[]'));
$this->oWizard->SetParameter('selected_components', json_encode($aSelectedComponents));
} else {
$aSelectedComponents = json_decode($this->oWizard->GetParameter('selected_components'), true);
}
// Save the choices for the summary step
$sDisplayChoices = '<ul>';
$i = 0;
foreach ($this->aSteps as $aStepInfo) {
$sDisplayChoices .= $this->GetSelectedModules($aStepInfo, $aSelectedComponents[$i], $aModules, '', '', $aExtensions);
$i++;
}
$sDisplayChoices .= '</ul>';
$this->oWizard->SetParameter('display_choices', $sDisplayChoices);
}
$sDisplayChoices .= '</ul>';
$this->oWizard->SetParameter('display_choices', $sDisplayChoices);
return new WizardState(WizStepDataAudit::class);
}

View File

@@ -178,6 +178,7 @@ class WizStepModulesChoice extends AbstractWizStepInstall
$i = 0;
$this->aSteps = null;
while (null != $this->GetStepInfo($i)) {
// Allow looping for all existing steps
$this->aSteps = null;
$aSteps [] = ["class" => "WizStepModulesChoice","state" => "$i"];
$i++;

View File

@@ -39,6 +39,11 @@ class WizStepSummary extends AbstractWizStepInstall
return [WizStepInstall::class];
}
public function CanAccessToWizardStep()
{
return true;
}
/**
* Returns the label for the " Next >> " button
* @return string The label for the button
@@ -123,21 +128,23 @@ class WizStepSummary extends AbstractWizStepInstall
$oPage->add($sExtensionsRemoved);
$oPage->add('</div>');
$oPage->add('<div class="closed"><a class="title ibo-setup-summary-title" href="#" aria-label="Database Parameters">Database Parameters</a><ul>');
$oPage->add('<li>Server Name: '.$aInstallParams['database']['server'].'</li>');
$oPage->add('<li>DB User Name: '.$aInstallParams['database']['user'].'</li>');
$oPage->add('<li>DB user password: ***</li>');
if (($sMode == 'install') && ($this->oWizard->GetParameter('create_db') == 'yes')) {
$oPage->add('<li>Database Name: '.$aInstallParams['database']['name'].' (will be created)</li>');
} else {
$oPage->add('<li>Database Name: '.$aInstallParams['database']['name'].'</li>');
if (UserRights::IsAdministrator()) {
$oPage->add('<div class="closed"><a class="title ibo-setup-summary-title" href="#" aria-label="Database Parameters">Database Parameters</a><ul>');
$oPage->add('<li>Server Name: '.$aInstallParams['database']['server'].'</li>');
$oPage->add('<li>DB User Name: '.$aInstallParams['database']['user'].'</li>');
$oPage->add('<li>DB user password: ***</li>');
if (($sMode == 'install') && ($this->oWizard->GetParameter('create_db') == 'yes')) {
$oPage->add('<li>Database Name: '.$aInstallParams['database']['name'].' (will be created)</li>');
} else {
$oPage->add('<li>Database Name: '.$aInstallParams['database']['name'].'</li>');
}
if ($aInstallParams['database']['prefix'] != '') {
$oPage->add('<li>Prefix for the '.ITOP_APPLICATION.' tables: '.$aInstallParams['database']['prefix'].'</li>');
} else {
$oPage->add('<li>Prefix for the '.ITOP_APPLICATION.' tables: none</li>');
}
$oPage->add('</ul></div>');
}
if ($aInstallParams['database']['prefix'] != '') {
$oPage->add('<li>Prefix for the '.ITOP_APPLICATION.' tables: '.$aInstallParams['database']['prefix'].'</li>');
} else {
$oPage->add('<li>Prefix for the '.ITOP_APPLICATION.' tables: none</li>');
}
$oPage->add('</ul></div>');
$oPage->add('<div class="closed"><a class="title ibo-setup-summary-title" href="#" aria-label="Data Model Configuration">Data Model Configuration</a>');
$oPage->add($this->oWizard->GetParameter('display_choices'));
@@ -253,7 +260,7 @@ JS
$sButtonUrl = utils::HtmlEntities($sButtonUrl);
$oPage->add_ready_script(
<<<JS
$('.ibo-setup--wizard--buttons-container tr td:nth-child(1)').after('<td style="text-align:center;"><button id="return-button" class="ibo-button ibo-is-alternative ibo-is-neutral" type="button" onclick="window.location.href=\'$sButtonUrl\'"><span class="ibo-button--label">$sButtonLabel</span></button></td>');
$('.ibo-setup--wizard--buttons-container tr td:nth-child(1)').before('<td style="text-align:center;"><button id="return-button" class="ibo-button ibo-is-alternative ibo-is-neutral" type="button" onclick="window.location.href=\'$sButtonUrl\'"><span class="ibo-button--label">$sButtonLabel</span></button></td>');
JS
);
}

View File

@@ -60,6 +60,9 @@ class WizStepWelcome extends WizardStep
public function Display(SetupPage $oPage): void
{
$this->oWizard->EraseParameters();
$this->oWizard->SetWizardSteps([]);
// Store the misc_options for the future...
$aMiscOptions = utils::ReadParam('option', [], false, 'raw_data');
$sMiscOptions = $this->oWizard->GetParameter('misc_options', json_encode($aMiscOptions));
@@ -127,7 +130,7 @@ HTML
<form id="fast_setup" method="post">
<input type="hidden" name="_class" value="WizStepLandingBeforeAudit"/>
<input type="hidden" name="operation" value="next"/>
<input type="hidden" name="_params[skip_wizard]" value="1"/>
<input type="hidden" name="skip_wizard" value="1"/>
</form>
HTML
);

View File

@@ -76,6 +76,11 @@ abstract class WizardStep
{
}
public function CanAccessToWizardStep()
{
return ($this->oWizard->GetParameter('return_application', '') === '');
}
protected function CheckDependencies()
{
if (is_null($this->bDependencyCheck)) {
@@ -187,15 +192,6 @@ abstract class WizardStep
return 'return true;';
}
/**
* Tells whether this step of the wizard requires that the configuration file be writable
* @return bool True if the wizard will possibly need to modify the configuration at some point
*/
public function RequiresWritableConfig()
{
return true;
}
/**
* Overload this function to implement asynchronous action(s) (AJAX)
* @param string $sCode The code of the action (if several actions need to be distinguished)

View File

@@ -1,11 +1,11 @@
<?php
/**
* @copyright Copyright (C) 2010-2024 Combodo SAS
/*
* @copyright Copyright (C) 2010-2026 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0
*/
namespace Combodo\iTop\Application\Helper;
namespace Combodo\iTop\Service\Session;
use Combodo\iTop\SessionTracker\SessionHandler;
@@ -21,9 +21,9 @@ class Session
/** @var int|null */
public static $iSessionId = null;
/** @var bool */
public static $bAllowCLI = false;
public static bool $bAllowCLI = false;
public static function Start()
public static function Start(): void
{
if (session_status() === PHP_SESSION_DISABLED) {
return;
@@ -47,7 +47,7 @@ class Session
self::$iSessionId = session_id();
}
public static function RegenerateId($bDeleteOldSession = false)
public static function RegenerateId($bDeleteOldSession = false): void
{
if (session_status() === PHP_SESSION_DISABLED || headers_sent()) {
return;
@@ -61,7 +61,7 @@ class Session
self::$iSessionId = session_id();
}
public static function WriteClose()
public static function WriteClose(): void
{
if (session_status() === PHP_SESSION_DISABLED) {
return;
@@ -76,7 +76,7 @@ class Session
* @param string|array $key key to access to the session variable. To access to $_SESSION['a']['b'] $key must be ['a', 'b']
* @param $value
*/
public static function Set($key, $value)
public static function Set($key, $value): void
{
if (!isset($_SESSION) || self::Get($key) == $value) {
return;
@@ -103,7 +103,7 @@ class Session
/**
* @param string|array $key key to access to the session variable. To access to $_SESSION['a']['b'] $key must be ['a', 'b']
*/
public static function Unset($key)
public static function Unset($key): void
{
if (self::IsSet($key)) {
$aSession = $_SESSION;
@@ -137,7 +137,7 @@ class Session
*
* @return mixed
*/
public static function Get($key, $default = null)
public static function Get($key, $default = null): mixed
{
if (isset($_SESSION)) {
$aSession = $_SESSION;
@@ -189,7 +189,7 @@ class Session
/**
* @return bool|string
*/
public static function GetLog()
public static function GetLog(): string
{
return print_r($_SESSION, true);
}

View File

@@ -0,0 +1,96 @@
<?php
/*
* @copyright Copyright (C) 2010-2026 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0
*/
namespace Combodo\iTop\Service\Session;
use IssueLog;
use LogChannels;
use utils;
class SessionParameters
{
private string $sSessionArrayName;
private array $aParameters;
public function __construct(string $sParamsName)
{
$this->sSessionArrayName = $sParamsName;
$this->aParameters = Session::Get($this->sSessionArrayName, []);
IssueLog::Enable(APPROOT.'log/error.log');
}
/**
* Reads a "persistent" parameter from the wizard's context
* @param string $sParamCode The code identifying this parameter
* @param mixed $defaultValue The default value of the parameter in case it was not set
*/
public function GetParameter(string $sParamCode, mixed $defaultValue = '')
{
if (array_key_exists($sParamCode, $this->aParameters)) {
return $this->aParameters[$sParamCode];
}
return $defaultValue;
}
/**
* Stores a "persistent" parameter in the wizard's context
*
* @param string $sParamCode The code identifying this parameter
* @param mixed $value The value to store
*/
public function SetParameter($sParamCode, $value): void
{
$this->aParameters[$sParamCode] = $value;
$this->Save();
}
/**
* Stores the value of the page's parameter in a "persistent" parameter in the wizard's context
* @param string $sParamCode The code identifying this parameter
* @param mixed $defaultValue The default value for the parameter
* @param string $sSanitizationFilter A 'sanitization' fitler. Default is 'raw_data', which means no filtering
*/
public function SetParameterFromPostedParams(string $sParamCode, mixed $defaultValue, string $sSanitizationFilter = 'raw_data'): void
{
$value = utils::ReadPostedParam($sParamCode, $defaultValue, $sSanitizationFilter);
$this->aParameters[$sParamCode] = $value;
$this->Save();
}
/**
* Stores the value of the page's parameter in a "persistent" parameter in the wizard's context
* @param string $sParamCode The code identifying this parameter
* @param mixed $defaultValue The default value for the parameter
* @param string $sSanitizationFilter A 'sanitization' fitler. Default is 'raw_data', which means no filtering
*/
public function SetParameterFromParams(string $sParamCode, mixed $defaultValue, string $sSanitizationFilter = 'raw_data'): void
{
$value = utils::ReadParam($sParamCode, $defaultValue, false, $sSanitizationFilter);
$this->aParameters[$sParamCode] = $value;
$this->Save();
}
private function Save(): void
{
Session::Set($this->sSessionArrayName, $this->aParameters);
}
public function Erase(): void
{
$this->aParameters = [];
$this->Save();
$this->LogParameters();
}
public function LogParameters(): void
{
IssueLog::Debug('---------------------------------', LogChannels::SESSION_PARAMETERS);
IssueLog::Debug(json_encode(Session::Get($this->sSessionArrayName, []), JSON_PRETTY_PRINT), LogChannels::SESSION_PARAMETERS);
IssueLog::Debug('---------------------------------', LogChannels::SESSION_PARAMETERS);
}
}

View File

@@ -121,3 +121,6 @@ class_alias(\Combodo\iTop\PropertyType\ValueType\Leaf\ValueTypeText::class, 'Com
class_alias(\Combodo\iTop\PropertyType\Serializer\XMLFormat\XMLFormatCSV::class, 'Combodo-XMLFormat-CSV');
class_alias(\Combodo\iTop\PropertyType\Serializer\XMLFormat\XMLFormatValueAsId::class, 'Combodo-XMLFormat-ValueAsId');
class_alias(\Combodo\iTop\PropertyType\Serializer\XMLFormat\XMLFormatFlatArray::class, 'Combodo-XMLFormat-FlatArray');
// Moved classes
class_alias(\Combodo\iTop\Service\Session\Session::class, '\Combodo\iTop\Application\Helper\Session');

View File

@@ -2,7 +2,7 @@
namespace Combodo\iTop\Test\UnitTest\Application;
use Combodo\iTop\Application\Helper\Session;
use Combodo\iTop\Service\Session\Session;
use Combodo\iTop\Test\UnitTest\ItopTestCase;
/**