use Combodo\iTop\Application\WebPage\WebPage; require_once(APPROOT.'setup/setuputils.class.inc.php'); require_once(APPROOT.'setup/parameters.class.inc.php'); require_once(APPROOT.'setup/applicationinstaller.class.inc.php'); require_once(APPROOT.'setup/parameters.class.inc.php'); require_once(APPROOT.'core/mutex.class.inc.php'); require_once(APPROOT.'setup/extensionsmap.class.inc.php'); require_once(APPROOT.'setup/wizardsteps/WizardStep.php'); require_once(APPROOT.'setup/wizardsteps/AbstractWizStepInstall.php'); require_once(APPROOT.'setup/wizardsteps/WizStepWelcome.php'); require_once(APPROOT.'setup/wizardsteps/WizStepInstallOrUpgrade.php'); require_once(APPROOT.'setup/wizardsteps/WizStepDetectedInfo.php'); require_once(APPROOT.'setup/wizardsteps/WizStepLicense.php'); require_once(APPROOT.'setup/wizardsteps/WizStepLicense2.php'); require_once(APPROOT.'setup/wizardsteps/AbstractWizStepMiscParams.php'); require_once(APPROOT.'setup/wizardsteps/WizStepAdminAccount.php'); require_once(APPROOT.'setup/wizardsteps/WizStepInstall.php'); require_once(APPROOT.'setup/wizardsteps/WizStepDataAudit.php'); require_once(APPROOT.'setup/wizardsteps/WizStepDBParams.php'); require_once(APPROOT.'setup/wizardsteps/WizStepDone.php'); require_once(APPROOT.'setup/wizardsteps/WizStepInstallMiscParams.php'); require_once(APPROOT.'setup/wizardsteps/WizStepModulesChoice.php'); require_once(APPROOT.'setup/wizardsteps/WizStepSummary.php'); require_once(APPROOT.'setup/wizardsteps/WizStepUpgradeMiscParams.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 * 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 { protected $aSteps; protected $sInitialStepClass; protected $sInitialState; protected $aParameters; /** * Initiailization 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 = '') { $this->sInitialStepClass = $sInitialStepClass; $this->sInitialState = $sInitialState; $this->aParameters = []; $this->aSteps = []; } /** * Pushes information about the current step onto the stack * @param array $aStepInfo Array('class' => , 'state' => ) */ protected function PushStep($aStepInfo) { array_push($this->aSteps, $aStepInfo); } /** * Removes information about the previous step from the stack * @return hash Array('class' => , 'state' => ) */ protected function PopStep() { return array_pop($this->aSteps); } /** * 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($sParamCode, $defaultValue = '') { if (array_key_exists($sParamCode, $this->aParameters)) { return $this->aParameters[$sParamCode]; } return $defaultValue; } /** * @return array Allow to update config using {@see Config::UpdateFromParams()} * * @since 3.1.0 N°2013 */ public function GetParamForConfigArray(): array { /** @noinspection PhpUnnecessaryLocalVariableInspection */ $aParamValues = [ 'db_server' => $this->GetParameter('db_server', ''), 'db_user' => $this->GetParameter('db_user', ''), 'db_pwd' => $this->GetParameter('db_pwd', ''), 'db_name' => $this->GetParameter('db_name', ''), 'db_prefix' => $this->GetParameter('db_prefix', ''), 'db_tls_enabled' => $this->GetParameter('db_tls_enabled', false), 'db_tls_ca' => $this->GetParameter('db_tls_ca', ''), ]; return $aParamValues; } /** * 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) { $this->aParameters[$sParamCode] = $value; } /** * 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 SaveParameter($sParamCode, $defaultValue, $sSanitizationFilter = 'raw_data') { $value = utils::ReadParam($sParamCode, $defaultValue, false, $sSanitizationFilter); $this->aParameters[$sParamCode] = $value; } /** * Starts the wizard by displaying it in its initial state */ public function Start() { $sCurrentStepClass = $this->sInitialStepClass; $oStep = new $sCurrentStepClass($this, $this->sInitialState); $this->DisplayStep($oStep); } /** * Progress towards the next step of the wizard * @throws Exception */ protected function Next() { $sCurrentStepClass = utils::ReadParam('_class', $this->sInitialStepClass); $sCurrentState = utils::ReadParam('_state', $this->sInitialState); /** @var \WizardStep $oStep */ $oStep = new $sCurrentStepClass($this, $sCurrentState); if ($oStep->ValidateParams()) { if ($oStep->CanComeBack()) { $this->PushStep(['class' => $sCurrentStepClass, 'state' => $sCurrentState]); } $aPossibleSteps = $oStep->GetPossibleSteps(); $aNextStepInfo = $oStep->UpdateWizardStateAndGetNextStep(true); // true => moving forward if (in_array($aNextStepInfo['class'], $aPossibleSteps)) { $oNextStep = new $aNextStepInfo['class']($this, $aNextStepInfo['state']); $this->DisplayStep($oNextStep); } else { throw new Exception("Internal error: Unexpected next step '{$aNextStepInfo['class']}'. The possible next steps are: ".implode(', ', $aPossibleSteps)); } } else { $this->DisplayStep($oStep); } } /** * Move one step back */ protected function Back() { // let the current step save its parameters $sCurrentStepClass = utils::ReadParam('_class', $this->sInitialStepClass); $sCurrentState = utils::ReadParam('_state', $this->sInitialState); $oStep = new $sCurrentStepClass($this, $sCurrentState); $aNextStepInfo = $oStep->ProcessParams(false); // false => Moving backwards // Display the previous step $aCurrentStepInfo = $this->PopStep(); $oStep = new $aCurrentStepInfo['class']($this, $aCurrentStepInfo['state']); $this->DisplayStep($oStep); } /** * Displays the specified 'step' of the wizard * @param WizardStep $oStep The 'step' to display */ protected function DisplayStep(WizardStep $oStep) { $oPage = new SetupPage($oStep->GetTitle()); if ($oStep->RequiresWritableConfig()) { $sConfigFile = utils::GetConfigFilePath(); if (file_exists($sConfigFile)) { // The configuration file already exists if (!is_writable($sConfigFile)) { SetupUtils::ExitReadOnlyMode(false); // Reset readonly mode in case of problem SetupUtils::EraseSetupToken(); $sRelativePath = utils::GetConfigFilePathRelative(); $oP = new SetupPage('Installation Cannot Continue'); $oP->add("

Fatal error

\n"); $oP->error("Error: the configuration file '".$sRelativePath."' already exists and cannot be overwritten."); $oP->p("The wizard cannot modify the configuration file for you. If you want to upgrade ".ITOP_APPLICATION.", make sure that the file '".$sRelativePath."' can be modified by the web server."); $sButtonsHtml = <<Reload HTML; $oP->p($sButtonsHtml); $oP->output(); // Prevent token creation exit; } } } $oPage->LinkScriptFromAppRoot('setup/setup.js'); $oPage->add('
'); $oPage->add('
'); $oStep->Display($oPage); $oPage->add('
'); $oPage->add_script("function CanMoveForward()\n{\n".$oStep->JSCanMoveForward()."\n}\n"); $oPage->add_script("function CanMoveBackward()\n{\n".$oStep->JSCanMoveBackward()."\n}\n"); // Add the back / next buttons and the hidden form // to store the parameters $oPage->add(''); $oPage->add(''); foreach ($this->aParameters as $sCode => $value) { $oPage->add(''); } $oPage->add(''); $oPage->add(''); if ((count($this->aSteps) > 0) && ($oStep->CanMoveBackward())) { $oPage->add(''); } if ($oStep->CanMoveForward()) { $oPage->add(''); } $oPage->add('
'); $oPage->add("
"); $oPage->add(''); // The div may become visible in case of error // Hack to have the "Next >>" button, be the default button, since the first submit button in the form is the default one $oPage->add_ready_script( <<output(); } /** * Make the wizard run: Start, Next or Back depending WizardUpdateButtons(); on the page's parameters */ public function Run() { /** * @since 3.2.0 Add the ContextTag init * @noinspection PhpUnusedLocalVariableInspection */ $oContextTag = new ContextTag(ContextTag::TAG_SETUP); $sOperation = utils::ReadParam('operation'); $this->aParameters = utils::ReadParam('_params', [], false, 'raw_data'); $this->aSteps = json_decode(utils::ReadParam('_steps', '[]', false, 'raw_data'), true /* bAssoc */); switch ($sOperation) { case 'next': $this->Next(); break; case 'back': $this->Back(); break; default: $this->Start(); } } /** * 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 hash $aAllSteps List of steps (used for recursion) */ public function DumpStructure($sStep = '', $aAllSteps = null) { if ($aAllSteps == null) { $aAllSteps = []; } if ($sStep == '') { $sStep = $this->sInitialStepClass; } $oStep = new $sStep($this, ''); $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 = new $sStep($this, ''); $sOutput .= "\t$sStep [ label = \"".$oStep->GetTitle()."\"];\n"; if (count($aNextSteps) > 0) { foreach ($aNextSteps as $sNextStep) { $sOutput .= "\t$sStep -> $sNextStep;\n"; } } } $sOutput .= "}\n"; return $sOutput; } }