diff --git a/setup/ajax.dataloader.php b/setup/ajax.dataloader.php index 50dae5701..f7f282691 100644 --- a/setup/ajax.dataloader.php +++ b/setup/ajax.dataloader.php @@ -139,7 +139,6 @@ try { ini_set('display_startup_errors', true); require_once(APPROOT.'/setup/wizardcontroller.class.inc.php'); - require_once(APPROOT.'/setup/wizardsteps.class.inc.php'); $sClass = utils::ReadParam('step_class', ''); $sState = utils::ReadParam('step_state', ''); diff --git a/setup/unattended-install/InstallationFileService.php b/setup/unattended-install/InstallationFileService.php index a4fa65c98..6a668ad20 100644 --- a/setup/unattended-install/InstallationFileService.php +++ b/setup/unattended-install/InstallationFileService.php @@ -6,7 +6,6 @@ use Combodo\iTop\Setup\ModuleDiscovery\ModuleFileReaderException; require_once(APPROOT.'/application/utils.inc.php'); require_once(APPROOT.'/setup/setuppage.class.inc.php'); require_once(APPROOT.'/setup/wizardcontroller.class.inc.php'); -require_once(APPROOT.'/setup/wizardsteps.class.inc.php'); class InstallationFileService { diff --git a/setup/wizard.php b/setup/wizard.php index 5d52ec952..8fbcd416b 100644 --- a/setup/wizard.php +++ b/setup/wizard.php @@ -32,7 +32,6 @@ require_once(APPROOT.'/application/utils.inc.php'); require_once(APPROOT.'/core/config.class.inc.php'); require_once(APPROOT.'/setup/setuppage.class.inc.php'); require_once(APPROOT.'/setup/wizardcontroller.class.inc.php'); -require_once(APPROOT.'/setup/wizardsteps.class.inc.php'); Session::Start(); clearstatcache(); // Make sure we know what we are doing ! diff --git a/setup/wizardcontroller.class.inc.php b/setup/wizardcontroller.class.inc.php index bd2cfa8f2..a5950e64c 100644 --- a/setup/wizardcontroller.class.inc.php +++ b/setup/wizardcontroller.class.inc.php @@ -18,6 +18,31 @@ // along with iTop. If not, see 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/AbstractWizStepBuild.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/WizStepAudit.php'); +require_once(APPROOT.'setup/wizardsteps/WizStepBuild.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 @@ -360,312 +385,3 @@ on the page's parameters } } -/** - * Abstract class to build "steps" for the wizard controller - * If a step needs to maintain an internal "state" (for complex steps) - * then it's up to the derived class to implement the behavior based on - * the internal 'sCurrentState' variable. - * @copyright Copyright (C) 2010-2024 Combodo SAS - * @license http://opensource.org/licenses/AGPL-3.0 - */ - -abstract class WizardStep -{ - /** - * A reference to the WizardController - * @var WizardController - */ - protected $oWizard; - /** - * Current 'state' of the wizard step. Simple 'steps' can ignore it - * @var string - */ - protected $sCurrentState; - - public function __construct(WizardController $oWizard, $sCurrentState) - { - $this->oWizard = $oWizard; - $this->sCurrentState = $sCurrentState; - } - - public function GetState() - { - return $this->sCurrentState; - } - - /** - * Displays the wizard page for the current class/state - * The page can contain any number of "" fields, but no "
...
" tag - * The name of the input fields (and their id if one is supplied) MUST NOT start with "_" - * (this is reserved for the wizard's own parameters) - * @return void - */ - abstract public function Display(WebPage $oPage); - - /** - * Processes the page's parameters and (if moving forward) returns the next step/state to be displayed - * @param bool $bMoveForward True if the wizard is moving forward 'Next >>' button pressed, false otherwise - * @return hash array('class' => $sNextClass, 'state' => $sNextState) - */ - abstract public function ProcessParams($bMoveForward = true); - - /** - * Returns the list of possible steps from this step forward - * @return array Array of strings (step classes) - */ - abstract public function GetPossibleSteps(); - - /** - * Returns title of the current step - * @return string The title of the wizard page for the current step - */ - abstract public function GetTitle(); - - /** - * Tells whether the parameters are Ok to move forward - * @return boolean True to move forward, false to stey on the same step - */ - public function ValidateParams() - { - return true; - } - - /** - * Tells whether this step/state is the last one of the wizard (dead-end) - * @return boolean True if the 'Next >>' button should be displayed - */ - public function CanMoveForward() - { - return true; - } - - /** - * Tells whether the "Next" button should be enabled interactively - * @return string A piece of javascript code returning either true or false - */ - public function JSCanMoveForward() - { - return 'return true;'; - } - - /** - * Returns the label for the " Next >> " button - * @return string The label for the button - */ - public function GetNextButtonLabel() - { - return 'Next'; - } - - /** - * Tells whether this step/state allows to go back or not - * @return boolean True if the '<< Back' button should be displayed - */ - public function CanMoveBackward() - { - return true; - } - - /** - * Tells whether the "Back" button should be enabled interactively - * @return string A piece of javascript code returning either true or false - */ - public function JSCanMoveBackward() - { - 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) - * @param hash $aParameters The action's parameters name => value - */ - public function AsyncAction(WebPage $oPage, $sCode, $aParameters) - { - } -} - -/* - * Example of a simple Setup Wizard with some parameters to store - * the installation mode (install | upgrade) and a simple asynchronous - * (AJAX) action. - * - * The setup wizard is executed by the following code: - * - * $oWizard = new WizardController('Step1'); - * $oWizard->Run(); - * -class Step1 extends WizardStep -{ - public function GetTitle() - { - return 'Welcome'; - } - - public function GetPossibleSteps() - { - return array('Step2', 'Step2bis'); - } - - public function ProcessParams($bMoveForward = true) - { - $sNextStep = ''; - $sInstallMode = utils::ReadParam('install_mode'); - if ($sInstallMode == 'install') - { - $this->oWizard->SetParameter('install_mode', 'install'); - $sNextStep = 'Step2'; - } - else - { - $this->oWizard->SetParameter('install_mode', 'upgrade'); - $sNextStep = 'Step2bis'; - - } - return array('class' => $sNextStep, 'state' => ''); - } - - public function Display(WebPage $oPage) - { - $oPage->p('This is Step 1!'); - $sInstallMode = $this->oWizard->GetParameter('install_mode', 'install'); - $sChecked = ($sInstallMode == 'install') ? ' checked ' : ''; - $oPage->p(' Install'); - $sChecked = ($sInstallMode == 'upgrade') ? ' checked ' : ''; - $oPage->p(' Upgrade'); - } -} - -class Step2 extends WizardStep -{ - public function GetTitle() - { - return 'Installation Parameters'; - } - - public function GetPossibleSteps() - { - return array('Step3'); - } - - public function ProcessParams($bMoveForward = true) - { - return array('class' => 'Step3', 'state' => ''); - } - - public function Display(WebPage $oPage) - { - $oPage->p('This is Step 2! (Installation)'); - } -} - -class Step2bis extends WizardStep -{ - public function GetTitle() - { - return 'Upgrade Parameters'; - } - - public function GetPossibleSteps() - { - return array('Step2ter'); - } - - public function ProcessParams($bMoveForward = true) - { - $sUpgradeInfo = utils::ReadParam('upgrade_info'); - $this->oWizard->SetParameter('upgrade_info', $sUpgradeInfo); - $sAdditionalUpgradeInfo = utils::ReadParam('additional_upgrade_info'); - $this->oWizard->SetParameter('additional_upgrade_info', $sAdditionalUpgradeInfo); - return array('class' => 'Step2ter', 'state' => ''); - } - - public function Display(WebPage $oPage) - { - $oPage->p('This is Step 2bis! (Upgrade)'); - $sUpgradeInfo = $this->oWizard->GetParameter('upgrade_info', ''); - $oPage->p('Type your name here: '); - $sAdditionalUpgradeInfo = $this->oWizard->GetParameter('additional_upgrade_info', ''); - $oPage->p('The installer replies: '); - - $oPage->add_ready_script("$('#upgrade_info').change(function() { - $('#v_upgrade_info').html(''); - WizardAsyncAction('', { upgrade_info: $('#upgrade_info').val() }); });"); - } - - public function AsyncAction(WebPage $oPage, $sCode, $aParameters) - { - usleep(300000); // 300 ms - $sName = $aParameters['upgrade_info']; - $sReply = addslashes("Hello ".$sName); - - $oPage->add_ready_script( -<< 'Step3', 'state' => ''); - } - - public function Display(WebPage $oPage) - { - $oPage->p('This is Step 2ter! (Upgrade)'); - } -} - -class Step3 extends WizardStep -{ - public function GetTitle() - { - return 'Installation Complete'; - } - - public function GetPossibleSteps() - { - return array(); - } - - public function ProcessParams($bMoveForward = true) - { - return array('class' => '', 'state' => ''); - } - - public function Display(WebPage $oPage) - { - $oPage->p('This is the FINAL Step'); - } - - public function CanMoveForward() - { - return false; - } -} - -End of the example */ diff --git a/setup/wizardsteps.class.inc.php b/setup/wizardsteps.class.inc.php deleted file mode 100644 index ad65ebdd3..000000000 --- a/setup/wizardsteps.class.inc.php +++ /dev/null @@ -1,2963 +0,0 @@ - - * WizStepLicense WizStepDetectedInfo - * WizStepDBParams + + - * WizStepAdminAccount | | - * WizStepInstallMiscParams v +------> - * + WizStepLicense2 +--> WizStepUpgradeMiscParams - * | + - * +---> <-----------------------------------+ - * WizStepModulesChoice - * WizStepSummary - * WizStepDone - */ - -use Combodo\iTop\Application\WebPage\WebPage; -use Combodo\iTop\PhpParser\Evaluation\PhpExpressionEvaluator; -use Combodo\iTop\Setup\ModuleDiscovery\ModuleFileReaderException; - -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'); - -/** - * First step of the iTop Installation Wizard: Welcome screen, requirements - */ -class WizStepWelcome extends WizardStep -{ - protected $bCanMoveForward; - - public function GetTitle() - { - return 'Welcome to '.ITOP_APPLICATION.' version '.ITOP_VERSION; - } - - /** - * Returns the label for the " Next >> " button - * @return string The label for the button - */ - public function GetNextButtonLabel() - { - return 'Continue'; - } - - public function GetPossibleSteps() - { - return ['WizStepInstallOrUpgrade']; - } - - public function ProcessParams($bMoveForward = true) - { - $sUID = SetupUtils::CreateSetupToken(); - $this->oWizard->SetParameter('authent', $sUID); - return ['class' => 'WizStepInstallOrUpgrade', 'state' => '']; - } - - public function Display(WebPage $oPage) - { - // Store the misc_options for the future... - $aMiscOptions = utils::ReadParam('option', [], false, 'raw_data'); - $sMiscOptions = $this->oWizard->GetParameter('misc_options', json_encode($aMiscOptions)); - $this->oWizard->SetParameter('misc_options', $sMiscOptions); - - $oPage->add(""); - $oPage->add_ready_script( - << 0) - { - alert("Internet Explorer version 10 or older is NOT supported! (Check that IE is not running in compatibility mode)"); - } -EOF - ); - $oPage->add('

'.ITOP_APPLICATION.' Installation Wizard

'); - $aResults = SetupUtils::CheckPhpAndExtensions(); - $this->bCanMoveForward = true; - $aInfo = []; - $aWarnings = []; - $aErrors = []; - foreach ($aResults as $oCheckResult) { - switch ($oCheckResult->iSeverity) { - case CheckResult::ERROR: - $aErrors[] = $oCheckResult->sLabel; - $this->bCanMoveForward = false; - break; - - case CheckResult::WARNING: - $aWarnings[] = $oCheckResult->sLabel; - break; - - case CheckResult::INFO: - $aInfo[] = $oCheckResult->sLabel; - break; - - case CheckResult::TRACE: - SetupLog::Ok($oCheckResult->sLabel); - break; - } - } - $sStyle = 'style="display:none;overflow:auto;"'; - $sToggleButtons = ''; - if (count($aErrors) > 0) { - $sStyle = 'overflow:auto;"'; - $sTitle = count($aErrors).' Error(s), '.count($aWarnings).' Warning(s).'; - $sH2Class = 'text-error'; - } elseif (count($aWarnings) > 0) { - $sTitle = count($aWarnings).' Warning(s) '.$sToggleButtons; - $sH2Class = 'text-warning'; - } else { - $sTitle = 'Ok. '.$sToggleButtons; - $sH2Class = 'text-valid'; - } - $oPage->add( - <<Prerequisites validation: $sTitle -
-HTML - ); - foreach ($aErrors as $sText) { - $oPage->error($sText); - } - foreach ($aWarnings as $sText) { - $oPage->warning($sText); - } - foreach ($aInfo as $sText) { - $oPage->ok($sText); - } - $oPage->add('
'); - if (!$this->bCanMoveForward) { - $oPage->p('Sorry, the installation cannot continue. Please fix the errors and reload this page to launch the installation again.'); - $oPage->p(''); - } - $oPage->add_ready_script('CheckDirectoryConfFilesPermissions("'.utils::GetItopVersionWikiSyntax().'")'); - } - - public function CanMoveForward() - { - return $this->bCanMoveForward; - } -} - -/** - * Second step of the iTop Installation Wizard: Install or Upgrade - */ -class WizStepInstallOrUpgrade extends WizardStep -{ - public function GetTitle() - { - return 'Install or Upgrade choice'; - } - - public function GetPossibleSteps() - { - return ['WizStepDetectedInfo', 'WizStepLicense']; - } - - public function ProcessParams($bMoveForward = true) - { - $sNextStep = ''; - $sInstallMode = utils::ReadParam('install_mode'); - - $this->oWizard->SaveParameter('previous_version_dir', ''); - $this->oWizard->SaveParameter('db_server', ''); - $this->oWizard->SaveParameter('db_user', ''); - $this->oWizard->SaveParameter('db_pwd', ''); - $this->oWizard->SaveParameter('db_name', ''); - $this->oWizard->SaveParameter('db_prefix', ''); - $this->oWizard->SaveParameter('db_backup', false); - $this->oWizard->SaveParameter('db_backup_path', ''); - $this->oWizard->SaveParameter('db_tls_enabled', false); - $this->oWizard->SaveParameter('db_tls_ca', ''); - - if ($sInstallMode == 'install') { - $this->oWizard->SetParameter('install_mode', 'install'); - $sFullSourceDir = SetupUtils::GetLatestDataModelDir(); - $this->oWizard->SetParameter('source_dir', $sFullSourceDir); - $this->oWizard->SetParameter('datamodel_version', SetupUtils::GetDataModelVersion($sFullSourceDir)); - $sNextStep = 'WizStepLicense'; - } else { - $this->oWizard->SetParameter('install_mode', 'upgrade'); - $sNextStep = 'WizStepDetectedInfo'; - - } - return ['class' => $sNextStep, 'state' => '']; - } - - public function Display(WebPage $oPage) - { - $sInstallMode = $this->oWizard->GetParameter('install_mode', ''); - $sDBServer = $this->oWizard->GetParameter('db_server', ''); - $sDBUser = $this->oWizard->GetParameter('db_user', ''); - $sDBPwd = $this->oWizard->GetParameter('db_pwd', ''); - $sDBName = $this->oWizard->GetParameter('db_name', ''); - $sDBPrefix = $this->oWizard->GetParameter('db_prefix', ''); - $bDBBackup = $this->oWizard->GetParameter('db_backup', false); - $sDBBackupPath = $this->oWizard->GetParameter('db_backup_path', ''); - $sTlsEnabled = $this->oWizard->GetParameter('db_tls_enabled', false); - $sTlsCA = $this->oWizard->GetParameter('db_tls_ca', ''); - $sMySQLBinDir = $this->oWizard->GetParameter('mysql_bindir', null); - $sPreviousVersionDir = ''; - if ($sInstallMode == '') { - $sDBBackupPath = utils::GetDataPath().'backups/manual/setup-'.date('Y-m-d_H_i'); - $bDBBackup = true; - $aPreviousInstance = SetupUtils::GetPreviousInstance(APPROOT); - if ($aPreviousInstance['found']) { - $sInstallMode = 'upgrade'; - $sDBServer = $aPreviousInstance['db_server']; - $sDBUser = $aPreviousInstance['db_user']; - $sDBPwd = $aPreviousInstance['db_pwd']; - $sDBName = $aPreviousInstance['db_name']; - $sDBPrefix = $aPreviousInstance['db_prefix']; - $sTlsEnabled = $aPreviousInstance['db_tls_enabled']; - $sTlsCA = $aPreviousInstance['db_tls_ca']; - $this->oWizard->SaveParameter('graphviz_path', $aPreviousInstance['graphviz_path']); - $sMySQLBinDir = $aPreviousInstance['mysql_bindir']; - $this->oWizard->SaveParameter('mysql_bindir', $aPreviousInstance['mysql_bindir']); - $sPreviousVersionDir = APPROOT; - } else { - $sInstallMode = 'install'; - } - } - $sPreviousVersionDir = $this->oWizard->GetParameter('previous_version_dir', $sPreviousVersionDir); - - $sUpgradeInfoStyle = ''; - if ($sInstallMode == 'install') { - $sUpgradeInfoStyle = ' style="display: none;" '; - } - $oPage->add('
What do you want to do?
'); - $sChecked = ($sInstallMode == 'install') ? ' checked ' : ''; - $oPage->p(''); - $sChecked = ($sInstallMode == 'upgrade') ? ' checked ' : ''; - $sDisabled = (($sInstallMode == 'install') && (empty($sPreviousVersionDir))) ? ' disabled' : ''; - $oPage->p(''); - - $sUpgradeDir = utils::HtmlEntities($sPreviousVersionDir); - $oPage->add( - << -
Location on the disk: -
-HTML - ); - - SetupUtils::DisplayDBParameters( - $oPage, - false, - $sDBServer, - $sDBUser, - $sDBPwd, - $sDBName, - $sDBPrefix, - $sTlsEnabled, - $sTlsCA, - null - ); - - $aBackupChecks = SetupUtils::CheckBackupPrerequisites($sDBBackupPath, $sMySQLBinDir); - $bCanBackup = true; - $sMySQLDumpMessage = ''; - foreach ($aBackupChecks as $oCheck) { - switch ($oCheck->iSeverity) { - case CheckResult::ERROR: - $bCanBackup = false; - $sMySQLDumpMessage .= '
Error:'.$oCheck->sLabel.'
'; - break; - case CheckResult::TRACE: - SetupLog::Ok($oCheck->sLabel); - break; - default: - $sMySQLDumpMessage .= '
Success:'.$oCheck->sLabel.'
'; - break; - } - } - $sChecked = ($bCanBackup && $bDBBackup) ? ' checked ' : ''; - $sDisabled = $bCanBackup ? '' : ' disabled '; - $oPage->add(''); - $oPage->add('
Save the backup to:
'); - $fFreeSpace = SetupUtils::CheckDiskSpace($sDBBackupPath); - $sMessage = ''; - if ($fFreeSpace !== false) { - $sMessage .= SetupUtils::HumanReadableSize($fFreeSpace).' free in '.dirname($sDBBackupPath); - } - $oPage->add($sMySQLDumpMessage.''.$sMessage.''); - $oPage->add(''); - $sAuthentToken = $this->oWizard->GetParameter('authent', ''); - $oPage->add(''); - //$oPage->add(''); - $oPage->add_ready_script( - <<add_ready_script( - <<add_ready_script( - <<add_ready_script( - <<oWizard->SetParameter('mode', 'upgrade'); - $this->oWizard->SetParameter('upgrade_type', $sUpgradeType); - $bDisplayLicense = $this->oWizard->GetParameter('display_license'); - - switch ($sUpgradeType) { - case 'keep-previous': - $sSourceDir = utils::ReadParam('relative_source_dir', '', false, 'raw_data'); - $this->oWizard->SetParameter('source_dir', $this->oWizard->GetParameter('previous_version_dir').'/'.$sSourceDir); - $this->oWizard->SetParameter('datamodel_version', utils::ReadParam('datamodel_previous_version', '', false, 'raw_data')); - break; - - case 'use-compatible': - $sDataModelPath = utils::ReadParam('datamodel_path', '', false, 'raw_data'); - $this->oWizard->SetParameter('source_dir', $sDataModelPath); - $this->oWizard->SaveParameter('datamodel_version', ''); - break; - - default: - // Do nothing, maybe the user pressed the Back button - } - if ($bDisplayLicense) { - $aRet = ['class' => 'WizStepLicense2', 'state' => '']; - } else { - $aRet = ['class' => 'WizStepUpgradeMiscParams', 'state' => '']; - } - return $aRet; - } - - /** - * @param WebPage $oPage - * - * @throws Exception - */ - public function Display(WebPage $oPage) - { - $oPage->add_style( - <<bCanMoveForward = true; - $bDisplayLicense = true; - $sPreviousVersionDir = $this->oWizard->GetParameter('previous_version_dir', ''); - $aInstalledInfo = SetupUtils::GetApplicationVersion($this->oWizard); - - if ($aInstalledInfo === false) { - throw(new Exception('No previous version of '.ITOP_APPLICATION.' found in the supplied database. The upgrade cannot continue.')); - } elseif (strcasecmp($aInstalledInfo['product_name'], ITOP_APPLICATION) != 0) { - $oPage->p("Warning: The installed products seem different. Are you sure that you want to upgrade {$aInstalledInfo['product_name']} with ".ITOP_APPLICATION."?"); - } - - $sInstalledVersion = $aInstalledInfo['product_version']; - $sInstalledDataModelVersion = $aInstalledInfo['datamodel_version']; - - $oPage->add("

Information about the upgrade from version $sInstalledVersion to ".ITOP_VERSION_FULL."

"); - - if ($sInstalledVersion == ITOP_VERSION_FULL) { - // Reinstalling the same version let's skip the license agreement... - $bDisplayLicense = false; - } - $this->oWizard->SetParameter('license', $bDisplayLicense); // Remember for later - - $sCompatibleDMDir = SetupUtils::GetLatestDataModelDir(); - if ($sCompatibleDMDir === false) { - // No compatible version exists... cannot upgrade. Either it is too old, or too new (downgrade !) - $this->bCanMoveForward = false; - $oPage->p("No datamodel directory found."); - } else { - $sUpgradeDMVersion = SetupUtils::GetDataModelVersion($sCompatibleDMDir); - $sPreviousSourceDir = isset($aInstalledInfo['source_dir']) ? $aInstalledInfo['source_dir'] : 'modules'; - $aChanges = false; - if (is_dir($sPreviousVersionDir)) { - // Check if the previous version is a "genuine" one or not... - $aChanges = SetupUtils::CheckVersion($sInstalledDataModelVersion, $sPreviousVersionDir.'/'.$sPreviousSourceDir); - } - if (($aChanges !== false) && ((count($aChanges['added']) > 0) || (count($aChanges['removed']) > 0) || (count($aChanges['modified']) > 0))) { - // Some changes were detected, prompt the user to keep or discard them - $oPage->p(" Some modifications were detected between the ".ITOP_APPLICATION." version in '$sPreviousVersionDir' and a genuine $sInstalledVersion version."); - $oPage->p("What do you want to do?"); - - $aWritableDirs = ['modules', 'portal']; - $aErrors = SetupUtils::CheckWritableDirs($aWritableDirs); - $sChecked = ($this->oWizard->GetParameter('upgrade_type') == 'keep-previous') ? ' checked ' : ''; - $sDisabled = (count($aErrors) > 0) ? ' disabled ' : ''; - - $oPage->p(''); - $oPage->add(''); - - $oPage->add(''); - - if (count($aErrors) > 0) { - $oPage->p("Cannot copy the installed version due to the following access rights issue(s):"); - foreach ($aErrors as $sDir => $oCheckResult) { - $oPage->p(' '.$oCheckResult->sLabel); - } - } - - $sChecked = ($this->oWizard->GetParameter('upgrade_type') == 'use-compatible') ? ' checked ' : ''; - - $oPage->p(''); - - $oPage->add(''); - $oPage->add(''); - - $oPage->add('
Details of the modifications
'); - if (count($aChanges['added']) > 0) { - $oPage->add('
    New files added:'); - foreach ($aChanges['added'] as $sFilePath => $void) { - $oPage->add('
  • '.$sFilePath.'
  • '); - } - $oPage->add('
'); - } - if (count($aChanges['removed']) > 0) { - $oPage->add('
    Deleted files:'); - foreach ($aChanges['removed'] as $sFilePath => $void) { - $oPage->add('
  • '.$sFilePath.'
  • '); - } - $oPage->add('
'); - } - if (count($aChanges['modified']) > 0) { - $oPage->add('
    Modified files:'); - foreach ($aChanges['modified'] as $sFilePath => $void) { - $oPage->add('
  • '.$sFilePath.'
  • '); - } - $oPage->add('
'); - } - $oPage->add('
'); - } else { - // No changes detected... or no way to tell because of the lack of a manifest or previous source dir - // Use the "compatible" datamodel as-is. - $sCompatibleDMDirToDisplay = utils::HtmlEntities($sCompatibleDMDir); - $sUpgradeDMVersionToDisplay = utils::HtmlEntities($sUpgradeDMVersion); - $oPage->add( - <<The datamodel will be upgraded from version $sInstalledDataModelVersion to version $sUpgradeDMVersion. - - - -HTML - ); - - } - - $oPage->add_ready_script( - <<oWizard->GetParameter('db_name', '').$this->oWizard->GetParameter('db_prefix', ''), - $this->oWizard->GetParameter('db_server', ''), - $this->oWizard->GetParameter('db_user', ''), - $this->oWizard->GetParameter('db_pwd', ''), - $this->oWizard->GetParameter('db_tls_enabled', ''), - $this->oWizard->GetParameter('db_tls_ca', '') - ); - if ($oMutex->IsLocked()) { - $oPage->add('
'.ITOP_APPLICATION.' cron process is being executed on the target database. '.ITOP_APPLICATION.' cron process will be stopped during the setup execution.
'); - } - } - } - - public function CanMoveForward() - { - return $this->bCanMoveForward; - } - - /** - * Tells whether the "Next" button should be enabled interactively - * @return string A piece of javascript code returning either true or false - */ - public function JSCanMoveForward() - { - return - << 0); - return bRet; -EOF - ; - } -} - -/** - * License acceptation screen - */ -class WizStepLicense extends WizardStep -{ - public function GetTitle() - { - return 'License Agreement'; - } - - public function GetPossibleSteps() - { - return ['WizStepDBParams']; - } - - public function ProcessParams($bMoveForward = true) - { - $this->oWizard->SaveParameter('accept_license', 'no'); - return ['class' => 'WizStepDBParams', 'state' => '']; - } - - /** - * @return bool true if we need to display a GDPR confirmation - * @throws \Exception - * @since 2.7.7 3.0.2 3.1.0 N°5037 method creation - * @since 2.7.8 3.0.3 3.1.0 N°5758 rename from NeedsRgpdConsent to NeedsGdprConsent - */ - private function NeedsGdprConsent() - { - $sMode = $this->oWizard->GetParameter('install_mode'); - - if ($sMode !== 'install') { - return false; - } - - $aModules = SetupUtils::AnalyzeInstallation($this->oWizard); - return SetupUtils::IsConnectableToITopHub($aModules); - } - - /** - * @param WebPage $oPage - */ - public function Display(WebPage $oPage) - { - $aLicenses = SetupUtils::GetLicenses(); - $oPage->add_style( - <<add('

Licenses agreements for the components of '.ITOP_APPLICATION.'

'); - $oPage->add_style('div a.no-arrow { background:transparent; padding-left:0;}'); - $oPage->add_style('.toggle { cursor:pointer; text-decoration:underline; color:#1C94C4; }'); - $oPage->add('
'); - $oPage->add('Components of '.ITOP_APPLICATION.''); - $oPage->add('
    '); - $index = 0; - foreach ($aLicenses as $oLicense) { - $oPage->add('
  • '.$oLicense->product.', © '.$oLicense->author.' is licensed under the '.$oLicense->license_type.' license. (Details)'); - $oPage->add(''); - $oPage->add_ready_script('$(".license_text a").attr("target", "_blank").addClass("no-arrow");'); - $oPage->add_ready_script('$("#toggle_'.$index.'").on("click", function() { $("#license_'.$index.'").toggle(); } );'); - $index++; - } - $oPage->add('
'); - $oPage->add('
'); - $sChecked = ($this->oWizard->GetParameter('accept_license', 'no') == 'yes') ? ' checked ' : ''; - $oPage->add('
'); - if ($this->NeedsGdprConsent()) { - $oPage->add('
'); - $oPage->add('
'); - $oPage->add('European General Data Protection Regulation'); - $oPage->add('
'.ITOP_APPLICATION.' software is compliant with the processing of personal data according to the European General Data Protection Regulation (GDPR).

-By installing '.ITOP_APPLICATION.' you agree that some information will be collected by Combodo to help you manage your instances and for statistical purposes. -This data remains anonymous until it is associated to a user account on iTop Hub.

-

List of collected data available in our Data privacy section.


'); - $oPage->add(''); - $oPage->add(''); - $oPage->add('
'); - } - $oPage->add_ready_script('$(".check_select").on("click change", function() { WizardUpdateButtons(); });'); - - $oPage->add_script( - << 'WizStepUpgradeMiscParams', 'state' => '']; - } -} - -/** - * Database Connection parameters screen - */ -class WizStepDBParams extends WizardStep -{ - public function GetTitle() - { - return 'Database Configuration'; - } - - public function GetPossibleSteps() - { - return ['WizStepAdminAccount']; - } - - public function ProcessParams($bMoveForward = true) - { - $this->oWizard->SaveParameter('db_server', ''); - $this->oWizard->SaveParameter('db_user', ''); - $this->oWizard->SaveParameter('db_pwd', ''); - $this->oWizard->SaveParameter('db_name', ''); - $this->oWizard->SaveParameter('db_prefix', ''); - $this->oWizard->SaveParameter('new_db_name', ''); - $this->oWizard->SaveParameter('create_db', ''); - $this->oWizard->SaveParameter('db_new_name', ''); - $this->oWizard->SaveParameter('db_tls_enabled', false); - $this->oWizard->SaveParameter('db_tls_ca', ''); - - return ['class' => 'WizStepAdminAccount', 'state' => '']; - } - - public function Display(WebPage $oPage) - { - $oPage->add('

Configuration of the database connection:

'); - $sDBServer = $this->oWizard->GetParameter('db_server', ''); - $sDBUser = $this->oWizard->GetParameter('db_user', ''); - $sDBPwd = $this->oWizard->GetParameter('db_pwd', ''); - $sDBName = $this->oWizard->GetParameter('db_name', ''); - $sDBPrefix = $this->oWizard->GetParameter('db_prefix', ''); - $sTlsEnabled = $this->oWizard->GetParameter('db_tls_enabled', ''); - $sTlsCA = $this->oWizard->GetParameter('db_tls_ca', ''); - $sNewDBName = $this->oWizard->GetParameter('db_new_name', false); - - $oPage->add(''); - SetupUtils::DisplayDBParameters( - $oPage, - true, - $sDBServer, - $sDBUser, - $sDBPwd, - $sDBName, - $sDBPrefix, - $sTlsEnabled, - $sTlsCA, - $sNewDBName - ); - $sAuthentToken = $this->oWizard->GetParameter('authent', ''); - $oPage->add(''); - $oPage->add('
'); - $sCreateDB = $this->oWizard->GetParameter('create_db', 'yes'); - if ($sCreateDB == 'no') { - $oPage->add_ready_script('$("#existing_db").prop("checked", true);'); - } else { - $oPage->add_ready_script('$("#create_db").prop("checked", true);'); - } - } - - public function AsyncAction(WebPage $oPage, $sCode, $aParameters) - { - switch ($sCode) { - case 'check_db': - SetupUtils::AsyncCheckDB($oPage, $aParameters); - break; - } - } - - /** - * Tells whether the "Next" button should be enabled interactively - * @return string A piece of javascript code returning either true or false - */ - public function JSCanMoveForward() - { - return - <<oWizard->SaveParameter('admin_user', ''); - $this->oWizard->SaveParameter('admin_pwd', ''); - $this->oWizard->SaveParameter('confirm_pwd', ''); - $this->oWizard->SaveParameter('admin_language', 'EN US'); - - return ['class' => WizStepInstallMiscParams::class, 'state' => '']; - } - - public function Display(WebPage $oPage) - { - $sAdminUser = $this->oWizard->GetParameter('admin_user', 'admin'); - $sAdminPwd = $this->oWizard->GetParameter('admin_pwd', ''); - $sConfirmPwd = $this->oWizard->GetParameter('confirm_pwd', ''); - $sAdminLanguage = $this->oWizard->GetParameter('admin_language', 'EN US'); - $oPage->add('

Definition of the Administrator Account

'); - $oPage->add('
'); - $oPage->add('Administrator Account'); - $oPage->add(''); - $oPage->add(''); - $oPage->add(''); - $oPage->add(''); - $sSourceDir = APPROOT.'dictionaries/'; - $aLanguages = SetupUtils::GetAvailableLanguages($sSourceDir); - $oPage->add(''); - $oPage->add('
Login:
Password:
Confirm password:
Language: '); - $oPage->add(SetupUtils::GetLanguageSelect($sSourceDir, 'admin_language', $sAdminLanguage)); - $oPage->add('
'); - $oPage->add('
'); - $oPage->add_ready_script( - <<'); - } - else - { - $("#v_admin_user").html(''); - } - - bPasswordsMatch = ($('#admin_pwd').val() == $('#confirm_pwd').val()); - if (!bPasswordsMatch) - { - $('#v_admin_pwd').html(''); - } - else - { - $('#v_admin_pwd').html(''); - } - bRet = bPasswordsMatch && bRet; - - return bRet; -EOF - ; - } -} - -/** - * @since 3.0.0 N°4092 - */ -abstract class AbstractWizStepMiscParams extends WizardStep -{ - /** - * @since 3.0.0 N°4092 - */ - final protected function AddUseSymlinksFlagOption(WebPage $oPage): void - { - if (MFCompiler::CanUseSymbolicLinksFlagBeUsed()) { - $sChecked = (MFCompiler::IsUseSymbolicLinksFlagPresent()) ? ' checked' : ''; - - $oPage->add('
'); - $oPage->add('Dev parameters'); - $oPage->p('
'); - $oPage->add_ready_script( - <<<'JS' -$("#use-symbolic-links").on("click", function() { - var $this = $(this), - bUseSymbolicLinks = $this.prop("checked"); - var sAuthent = $('#authent_token').val(); - var oAjaxParams = { operation: 'toggle_use_symbolic_links', bUseSymbolicLinks: bUseSymbolicLinks, authent: sAuthent}; - $.post(GetAbsoluteUrlAppRoot()+'setup/ajax.dataloader.php', oAjaxParams); -}); -JS - ); - } - } - - final protected function AddForceUninstallFlagOption(WebPage $oPage): void - { - $sChecked = $this->oWizard->GetParameter('force-uninstall', false) ? ' checked ' : ''; - $oPage->add('
'); - $oPage->add('Advanced parameters'); - $oPage->p('
'); - - $oPage->add_ready_script( - <<<'JS' -$("#force-uninstall").on("click", function() { - let $this = $(this); - let bForceUninstall = $this.prop("checked"); - if( bForceUninstall && !confirm('Beware, uninstalling extensions flagged as non uninstallable may result in data corruption and application crashes. Are you sure you want to continue ?')){ - $this.prop("checked",false); - } -}); -JS - ); - } -} - -/** - * Miscellaneous Parameters (URL, Sample Data) when installing from scratch - */ -class WizStepInstallMiscParams extends AbstractWizStepMiscParams -{ - public function GetTitle() - { - return 'Miscellaneous Parameters'; - } - - public function GetPossibleSteps() - { - return ['WizStepModulesChoice']; - } - - public function ProcessParams($bMoveForward = true) - { - $this->oWizard->SaveParameter('default_language', ''); - $this->oWizard->SaveParameter('application_url', ''); - $this->oWizard->SaveParameter('graphviz_path', ''); - $this->oWizard->SaveParameter('sample_data', 'yes'); - return ['class' => 'WizStepModulesChoice', 'state' => 'start_install']; - } - - public function Display(WebPage $oPage) - { - $sDefaultLanguage = $this->oWizard->GetParameter('default_language', $this->oWizard->GetParameter('admin_language')); - $sApplicationURL = $this->oWizard->GetParameter('application_url', utils::GetDefaultUrlAppRoot(true)); - $sDefaultGraphvizPath = (strtolower(substr(PHP_OS, 0, 3)) === 'win') ? 'C:\\Program Files\\Graphviz\\bin\\dot.exe' : '/usr/bin/dot'; - $sGraphvizPath = $this->oWizard->GetParameter('graphviz_path', $sDefaultGraphvizPath); - $sSampleData = $this->oWizard->GetParameter('sample_data', 'yes'); - $oPage->add('

Additional parameters

'); - $oPage->add('
'); - $oPage->add('Default Language'); - $oPage->add(''); - $sSourceDir = APPROOT.'dictionaries/'; - $aLanguages = SetupUtils::GetAvailableLanguages($sSourceDir); - $oPage->add(''); - $oPage->add('
Default Language: '); - $oPage->add(SetupUtils::GetLanguageSelect($sSourceDir, 'default_language', $sDefaultLanguage)); - $oPage->add('
'); - $oPage->add('
'); - $oPage->add('
'); - $oPage->add('Application URL'); - $oPage->add(''); - $oPage->add(''); - $oPage->add('
URL:
'); - $oPage->add('
Change the value above if the end-users will be accessing the application by another path due to a specific configuration of the web server.
'); - $oPage->add('
'); - $oPage->add('
'); - $oPage->add('Path to Graphviz\' dot application'); - $oPage->add(''); - $oPage->add(''); - $oPage->add(''); - $oPage->add('
Path:
'); - $oPage->add(''); - $oPage->add('
'); - $oPage->add('
'); - $oPage->add('Sample Data'); - $sChecked = ($sSampleData == 'yes') ? 'checked ' : ''; - $oPage->p('
'); - $sAuthentToken = $this->oWizard->GetParameter('authent', ''); - $oPage->add(''); - $oPage->add_ready_script( - <<AddUseSymlinksFlagOption($oPage); - } - - public function AsyncAction(WebPage $oPage, $sCode, $aParameters) - { - switch ($sCode) { - case 'check_graphviz': - $sGraphvizPath = $aParameters['graphviz_path']; - $aCheck = SetupUtils::CheckGraphviz($sGraphvizPath); - - // N°2214 logging TRACE results - $aTraceCheck = CheckResult::FilterCheckResultArray($aCheck, [CheckResult::TRACE]); - foreach ($aTraceCheck as $oTraceCheck) { - SetupLog::Ok($oTraceCheck->sLabel); - } - - $aNonTraceCheck = array_diff($aCheck, $aTraceCheck); - foreach ($aNonTraceCheck as $oCheck) { - switch ($oCheck->iSeverity) { - case CheckResult::INFO: - $sStatus = 'ok'; - $sInfoExplanation = $oCheck->sLabel; - $sMessage = json_encode('
'.$sInfoExplanation.'
'); - - break; - - default: - case CheckResult::ERROR: - case CheckResult::WARNING: - $sStatus = 'ko'; - $sErrorExplanation = $oCheck->sLabel; - $sMessage = json_encode('
'.$sErrorExplanation.'
'); - break; - } - - if ($oCheck->iSeverity !== CheckResult::TRACE) { - $oPage->add_ready_script( - <<'); - } - else - { - $("#v_application_url").html(''); - } - bGraphviz = ($('#graphviz_path').val() != ''); - if (!bGraphviz) - { - // Does not prevent to move forward - $("#v_graphviz_path").html(''); - } - else - { - $("#v_graphviz_path").html(''); - } - return bRet; -EOF - ; - } -} - -/** - * Miscellaneous Parameters (URL...) in case of upgrade - */ -class WizStepUpgradeMiscParams extends AbstractWizStepMiscParams -{ - public function GetTitle() - { - return 'Miscellaneous Parameters'; - } - - public function GetPossibleSteps() - { - return ['WizStepModulesChoice']; - } - - public function ProcessParams($bMoveForward = true) - { - $this->oWizard->SaveParameter('application_url', ''); - $this->oWizard->SaveParameter('graphviz_path', ''); - $this->oWizard->SaveParameter('force-uninstall', false); - return ['class' => 'WizStepModulesChoice', 'state' => 'start_upgrade']; - } - - public function Display(WebPage $oPage) - { - $sApplicationURL = $this->oWizard->GetParameter('application_url', utils::GetAbsoluteUrlAppRoot(true)); //Preserve existing configuration (except for the str_replace based joker $SERVER_NAME$ which is lost) - $sDefaultGraphvizPath = (strtolower(substr(PHP_OS, 0, 3)) === 'win') ? 'C:\\Program Files\\Graphviz\\bin\\dot.exe' : '/usr/bin/dot'; - $sGraphvizPath = $this->oWizard->GetParameter('graphviz_path', $sDefaultGraphvizPath); - $oPage->add('

Additional parameters

'); - $oPage->add('
'); - $oPage->add('Application URL'); - $oPage->add(''); - $oPage->add(''); - $oPage->add('
URL:
'); - $oPage->add('
Change the value above if the end-users will be accessing the application by another path due to a specific configuration of the web server.
'); - $oPage->add('
'); - $oPage->add('
'); - $oPage->add('Path to Graphviz\' dot application'); - $oPage->add(''); - $oPage->add(''); - $oPage->add(''); - $oPage->add('
Path:
'); - $oPage->add(''); - $oPage->add('
'); - $sAuthentToken = $this->oWizard->GetParameter('authent', ''); - $oPage->add(''); - $oPage->add_ready_script( - <<AddUseSymlinksFlagOption($oPage); - $this->AddForceUninstallFlagOption($oPage); - } - - public function AsyncAction(WebPage $oPage, $sCode, $aParameters) - { - switch ($sCode) { - case 'check_graphviz': - $sGraphvizPath = $aParameters['graphviz_path']; - $aCheck = SetupUtils::CheckGraphviz($sGraphvizPath); - - // N°2214 logging TRACE results - $aTraceCheck = CheckResult::FilterCheckResultArray($aCheck, [CheckResult::TRACE]); - foreach ($aTraceCheck as $oTraceCheck) { - SetupLog::Ok($oTraceCheck->sLabel); - } - - $aNonTraceCheck = array_diff($aCheck, $aTraceCheck); - foreach ($aNonTraceCheck as $oCheck) { - switch ($oCheck->iSeverity) { - case CheckResult::INFO: - $sStatus = 'ok'; - $sInfoExplanation = $oCheck->sLabel; - $sMessage = json_encode('
'.$sInfoExplanation.'
'); - break; - - default: - case CheckResult::ERROR: - case CheckResult::WARNING: - $sStatus = 'ko'; - $sErrorExplanation = $oCheck->sLabel; - $sMessage = json_encode('
'.$sErrorExplanation.'
'); - break; - } - $oPage->add_ready_script( - <<'); - } - else - { - $("#v_application_url").html(''); - } - bGraphviz = ($('#graphviz_path').val() != ''); - if (!bGraphviz) - { - // Does not prevent to move forward - $("#v_graphviz_path").html(''); - } - else - { - $("#v_graphviz_path").html(''); - } - return bRet; -EOF - ; - } -} -/** - * Choice of the modules to be installed - */ -class WizStepModulesChoice extends WizardStep -{ - protected static string $SEP = '_'; - protected bool $bUpgrade = false; - protected bool $bCanMoveForward = true; - protected ?Config $oConfig = null; - - /** - * - * @var iTopExtensionsMap - */ - protected iTopExtensionsMap $oExtensionsMap; - - private ?array $aSteps = null; - - protected PhpExpressionEvaluator $oPhpExpressionEvaluator; - - /** - * Whether we were able to load the choices from the database or not - * @var bool - */ - protected bool $bChoicesFromDatabase; - - private array $aAnalyzeInstallationModules; - private ?MissingDependencyException $oMissingDependencyException = null; - - public function __construct(WizardController $oWizard, $sCurrentState) - { - parent::__construct($oWizard, $sCurrentState); - $this->bChoicesFromDatabase = false; - $this->oExtensionsMap = new iTopExtensionsMap(); - $sPreviousSourceDir = $this->oWizard->GetParameter('previous_version_dir', ''); - $sConfigPath = null; - if (($sPreviousSourceDir !== '') && is_readable($sPreviousSourceDir.'/conf/production/config-itop.php')) { - $sConfigPath = $sPreviousSourceDir.'/conf/production/config-itop.php'; - } elseif (is_readable(utils::GetConfigFilePath('production'))) { - $sConfigPath = utils::GetConfigFilePath('production'); - } - - // only called if the config file exists : we are updating a previous installation ! - // WARNING : we can't load this config directly, as it might be from another directory with a different approot_url (N°2684) - if ($sConfigPath !== null) { - $this->oConfig = new Config($sConfigPath); - - $aParamValues = $oWizard->GetParamForConfigArray(); - $this->oConfig->UpdateFromParams($aParamValues); - - $this->oExtensionsMap->LoadChoicesFromDatabase($this->oConfig); - $this->bChoicesFromDatabase = true; - } - - // Sanity check (not stopper, to let developers go further...) - try { - $this->aAnalyzeInstallationModules = SetupUtils::AnalyzeInstallation($this->oWizard, true); - } catch (MissingDependencyException $e) { - $this->oMissingDependencyException = $e; - $this->aAnalyzeInstallationModules = SetupUtils::AnalyzeInstallation($this->oWizard); - } - } - - public function GetTitle(): string - { - $aStepInfo = $this->GetStepInfo(); - - return $aStepInfo['title'] ?? 'Modules selection'; - } - - public function GetPossibleSteps() - { - return ['WizStepModulesChoice', 'WizStepAudit']; - } - - public function GetAddedAndRemovedExtensions($aSelectedExtensions) - { - $aExtensionsAdded = []; - $aExtensionsRemoved = []; - $aExtensionsNotUninstallable = []; - foreach ($this->oExtensionsMap->GetAllExtensionsWithPreviouslyInstalled() as $oExtension) { - /* @var \iTopExtension $oExtension */ - $bSelected = in_array($oExtension->sCode, $aSelectedExtensions); - if ($oExtension->bInstalled && !$bSelected) { - $aExtensionsRemoved[$oExtension->sCode] = $oExtension->sLabel; - if (!$oExtension->CanBeUninstalled()) { - $aExtensionsNotUninstallable[$oExtension->sCode] = true; - } - } elseif (!$oExtension->bInstalled && $bSelected) { - $aExtensionsAdded[$oExtension->sCode] = $oExtension->sLabel; - } - } - - return [$aExtensionsAdded, $aExtensionsRemoved, $aExtensionsNotUninstallable]; - } - - public function ProcessParams($bMoveForward = true) - { - // Accumulates the selected modules: - $index = $this->GetStepIndex(); - - // use json_encode:decode to store a hash array: step_id => array(input_name => selected_input_id) - $aSelectedChoices = json_decode($this->oWizard->GetParameter('selected_components', '{}'), true); - $aSelected = utils::ReadParam('choice', []); - $aSelectedChoices[$index] = $aSelected; - $this->oWizard->SetParameter('selected_components', json_encode($aSelectedChoices)); - - if ($this->GetStepInfo($index) == null) { - throw new Exception('Internal error: invalid step "'.$index.'" for the choice of modules.'); - } elseif ($bMoveForward) { - if ($this->GetStepInfo(1 + $index) != null) { - return ['class' => 'WizStepModulesChoice', 'state' => (1 + $index)]; - } else { - // Exiting this step of the wizard, let's convert the selection into a list of modules - $aModules = []; - $aExtensions = []; - $sDisplayChoices = '
    '; - for ($i = 0; $i <= $index; $i++) { - $aStepInfo = $this->GetStepInfo($i); - $sDisplayChoices .= $this->GetSelectedModules($aStepInfo, $aSelectedChoices[$i], $aModules, '', '', $aExtensions); - } - $sDisplayChoices .= '
'; - if (class_exists('CreateITILProfilesInstaller')) { - $this->oWizard->SetParameter('old_addon', true); - } - - [$aExtensionsAdded, $aExtensionsRemoved, $aExtensionsNotUninstallable] = $this->GetAddedAndRemovedExtensions($aExtensions); - $this->oWizard->SetParameter('selected_modules', json_encode(array_keys($aModules))); - $this->oWizard->SetParameter('selected_extensions', json_encode($aExtensions)); - $this->oWizard->SetParameter('display_choices', $sDisplayChoices); - $this->oWizard->SetParameter('extensions_added', json_encode($aExtensionsAdded)); - $this->oWizard->SetParameter('removed_extensions', json_encode($aExtensionsRemoved)); - $this->oWizard->SetParameter('extensions_not_uninstallable', json_encode(array_keys($aExtensionsNotUninstallable))); - $sMode = $this->oWizard->GetParameter('mode', 'install'); - if ($sMode == 'install') { - return ['class' => 'WizStepAudit', 'state' => '']; - } - else { - return ['class' => 'WizStepAudit', 'state' => '']; - } - - } - - } - } - - public function Display(WebPage $oPage) - { - $this->DisplayStep($oPage); - } - - /** - * @param \SetupPage $oPage - * - * @throws \Exception - */ - protected function DisplayStep($oPage) - { - // Sanity check (not stopper, to let developers go further...) - if (! is_null($this->oMissingDependencyException)) { - $oPage->warning($this->oMissingDependencyException->getHtmlDesc(), $this->oMissingDependencyException->getMessage()); - } - - $this->bUpgrade = ($this->oWizard->GetParameter('install_mode') != 'install'); - $aStepInfo = $this->GetStepInfo(); - $oPage->add_style("div.choice { margin: 0.5em;}"); - $oPage->add_style("div.choice a { text-decoration:none; font-weight: bold; color: #1C94C4 }"); - $oPage->add_style("div.description { margin-left: 2em; }"); - $oPage->add_style(".choice-disabled { color: #999; }"); - $oPage->add_style("input.unremovable { accent-color: orangered;}"); - - $sManualInstallError = SetupUtils::CheckManualInstallDirEmpty( - $this->aAnalyzeInstallationModules, - $this->oWizard->GetParameter('extensions_dir', 'extensions') - ); - if ($sManualInstallError !== '') { - $oPage->warning($sManualInstallError); - } - - $oPage->add('
'); - $sBannerPath = isset($aStepInfo['banner']) ? $aStepInfo['banner'] : ''; - if (!empty($sBannerPath)) { - if (substr($sBannerPath, 0, 1) == '/') { - // absolute path, means relative to APPROOT - $sBannerUrl = utils::GetDefaultUrlAppRoot(true).$sBannerPath; - } else { - // relative path: i.e. relative to the directory containing the XML file - $sFullPath = dirname($this->GetSourceFilePath()).'/'.$sBannerPath; - $sRealPath = realpath($sFullPath); - $sBannerUrl = utils::GetDefaultUrlAppRoot(true).str_replace(realpath(APPROOT), '', $sRealPath); - } - $oPage->add(''); - } - $sDescription = $aStepInfo['description'] ?? ''; - $oPage->add(''.$sDescription.''); - $oPage->add('
'); - - // Build the default choices - $aDefaults = $this->GetDefaults($aStepInfo, $this->aAnalyzeInstallationModules); - $index = $this->GetStepIndex(); - - // retrieve the saved selection - // use json_encode:decode to store a hash array: step_id => array(input_name => selected_input_id) - $aParameters = json_decode($this->oWizard->GetParameter('selected_components', '{}'), true); - if (!isset($aParameters[$index])) { - $aParameters[$index] = $aDefaults; - } - $aSelectedComponents = $aParameters[$index]; - - $oPage->add('
'); - $this->DisplayOptions($oPage, $aStepInfo, $aSelectedComponents, $aDefaults); - $oPage->add('
'); - - $oPage->add_script( - <<add_ready_script( - <<bChoicesFromDatabase) { - $this->GuessDefaultsFromModules($aInfo, $aDefaults, $aModules, $sParentId); - } else { - $this->GetDefaultsFromDatabase($aInfo, $aDefaults, $sParentId); - } - return $aDefaults; - } - - protected function GetDefaultsFromDatabase($aInfo, &$aDefaults, $sParentId) - { - $aOptions = isset($aInfo['options']) ? $aInfo['options'] : []; - foreach ($aOptions as $index => $aChoice) { - $sChoiceId = $sParentId.self::$SEP.$index; - if ($this->bUpgrade) { - if ($this->oExtensionsMap->IsMarkedAsChosen($aChoice['extension_code'])) { - $aDefaults[$sChoiceId] = $sChoiceId; - } - } elseif (isset($aChoice['default']) && $aChoice['default']) { - $aDefaults[$sChoiceId] = $sChoiceId; - } - // Recurse for sub_options (if any) - if (isset($aChoice['sub_options'])) { - $this->GetDefaultsFromDatabase($aChoice['sub_options'], $aDefaults, $sChoiceId); - } - } - - $aAlternatives = $aInfo['alternatives'] ?? []; - $sChoiceName = null; - foreach ($aAlternatives as $index => $aChoice) { - $sChoiceId = $sParentId.self::$SEP.$index; - if ($sChoiceName == null) { - $sChoiceName = $sChoiceId; // All radios share the same name - } - if ($this->bUpgrade) { - if ($this->oExtensionsMap->IsMarkedAsChosen($aChoice['extension_code'])) { - $aDefaults[$sChoiceName] = $sChoiceId; - } - } elseif (isset($aChoice['default']) && $aChoice['default']) { - $aDefaults[$sChoiceName] = $sChoiceId; - } - // Recurse for sub_options (if any) - if (isset($aChoice['sub_options'])) { - $this->GetDefaultsFromDatabase($aChoice['sub_options'], $aDefaults, $sChoiceId); - } - } - } - - /** - * Try to guess the user choices based on the current list of installed modules... - * @param array $aInfo - * @param array $aDefaults - * @param array $aModules - * @param string $sParentId - * @return array - */ - protected function GuessDefaultsFromModules($aInfo, &$aDefaults, $aModules, $sParentId = '') - { - $aRetScore = []; - $aScores = []; - - $aOptions = isset($aInfo['options']) ? $aInfo['options'] : []; - foreach ($aOptions as $index => $aChoice) { - $sChoiceId = $sParentId.self::$SEP.$index; - $aScores[$sChoiceId] = []; - if (!$this->bUpgrade && isset($aChoice['default']) && $aChoice['default']) { - $aDefaults[$sChoiceId] = $sChoiceId; - } - if ($this->bUpgrade) { - // In upgrade mode, the defaults are the installed modules - foreach ($aChoice['modules'] as $sModuleId) { - if ($aModules[$sModuleId]['installed_version'] != '') { - // A module corresponding to this choice is installed - $aScores[$sChoiceId][$sModuleId] = true; - } - } - // Used for migration from 1.3.x or before - // Accept that the new version can have one new module than the previous version - // The option is still selected - $iSelected = count($aScores[$sChoiceId]); - $iNeeded = count($aChoice['modules']); - if (($iSelected > 0) && (($iNeeded - $iSelected) < 2)) { - // All the modules are installed, this choice is selected - $aDefaults[$sChoiceId] = $sChoiceId; - } - $aRetScore = array_merge($aRetScore, $aScores[$sChoiceId]); - } - - if (isset($aChoice['sub_options'])) { - $aScores[$sChoiceId] = array_merge($aScores[$sChoiceId], $this->GuessDefaultsFromModules($aChoice['sub_options'], $aDefaults, $sChoiceId)); - } - $index++; - } - - $aAlternatives = isset($aInfo['alternatives']) ? $aInfo['alternatives'] : []; - $sChoiceName = null; - $sChoiceIdNone = null; - foreach ($aAlternatives as $index => $aChoice) { - $sChoiceId = $sParentId.self::$SEP.$index; - $aScores[$sChoiceId] = []; - if ($sChoiceName == null) { - $sChoiceName = $sChoiceId; - } - if (!$this->bUpgrade && isset($aChoice['default']) && $aChoice['default']) { - $aDefaults[$sChoiceName] = $sChoiceId; - } - if (isset($aChoice['sub_options'])) { - // By default (i.e. install-mode), sub options can only be checked if the parent option is checked by default - if ($this->bUpgrade || (isset($aChoice['default']) && $aChoice['default'])) { - $aScores[$sChoiceId] = $this->GuessDefaultsFromModules($aChoice['sub_options'], $aDefaults, $aModules, $sChoiceId); - } - } - $index++; - } - - $iMaxScore = 0; - if ($this->bUpgrade && (count($aAlternatives) > 0)) { - // The installed choices have precedence over the 'default' choices - // In case several choices share the same base modules, let's weight the alternative choices - // based on their number of installed modules - $sChoiceName = null; - - foreach ($aAlternatives as $index => $aChoice) { - $sChoiceId = $sParentId.self::$SEP.$index; - if ($sChoiceName == null) { - $sChoiceName = $sChoiceId; - } - if (array_key_exists('modules', $aChoice)) { - foreach ($aChoice['modules'] as $sModuleId) { - if ($aModules[$sModuleId]['installed_version'] != '') { - // A module corresponding to this choice is installed, increase the score of this choice - if (!isset($aScores[$sChoiceId])) { - $aScores[$sChoiceId] = []; - } - $aScores[$sChoiceId][$sModuleId] = true; - $iMaxScore = max($iMaxScore, count($aScores[$sChoiceId])); - } - } - //if (count($aScores[$sChoiceId]) == count($aChoice['modules'])) - //{ - // $iScore += 100; // Bonus for the parent when a choice is complete - //} - $aRetScore = array_merge($aRetScore, $aScores[$sChoiceId]); - } - $iMaxScore = max($iMaxScore, isset($aScores[$sChoiceId]) ? count($aScores[$sChoiceId]) : 0); - } - } - if ($iMaxScore > 0) { - $aNumericScores = []; - foreach ($aScores as $sChoiceId => $aModules) { - $aNumericScores[$sChoiceId] = count($aModules); - } - // The choice with the bigger score wins ! - asort($aNumericScores, SORT_NUMERIC); - $aKeys = array_keys($aNumericScores); - $sBetterChoiceId = array_pop($aKeys); - $aDefaults[$sChoiceName] = $sBetterChoiceId; - } - // echo "Scores:
".print_r($aScores, true)."

"; - // echo "Defaults:
".print_r($aDefaults, true)."

"; - return $aRetScore; - } - - private function GetPhpExpressionEvaluator(): PhpExpressionEvaluator - { - if (!isset($this->oPhpExpressionEvaluator)) { - $this->oPhpExpressionEvaluator = new PhpExpressionEvaluator([], RunTimeEnvironment::STATIC_CALL_AUTOSELECT_WHITELIST); - } - - return $this->oPhpExpressionEvaluator; - } - - /** - * Converts the list of selected "choices" into a list of "modules": take into account the selected and the mandatory modules - * - * @param array $aInfo Info about the "choice" array('options' => array(...), 'alternatives' => array(...)) - * @param array $aSelectedChoices List of selected choices array('name' => 'selected_value_id') - * @param array $aModules Return parameter: List of selected modules array('module_id' => true) - * @param string $sParentId Used for recursion - * - * @return string A text representation of what will be installed - */ - protected function GetSelectedModules($aInfo, $aSelectedChoices, &$aModules, $sParentId = '', $sDisplayChoices = '', &$aSelectedExtensions = null) - { - if ($sParentId == '') { - // Check once (before recursing) that the hidden modules are selected - foreach ($this->aAnalyzeInstallationModules as $sModuleId => $aModule) { - if (($sModuleId != ROOT_MODULE) && !isset($aModules[$sModuleId])) { - if (($aModule['category'] == 'authentication') || (!$aModule['visible'] && !isset($aModule['auto_select']))) { - $aModules[$sModuleId] = true; - $sDisplayChoices .= '
  • '.$aModule['label'].' (hidden)
  • '; - } - } - } - } - $aOptions = isset($aInfo['options']) ? $aInfo['options'] : []; - foreach ($aOptions as $index => $aChoice) { - $sChoiceId = $sParentId.self::$SEP.$index; - $aModuleInfo = []; - // Get the extension corresponding to the choice - foreach ($this->oExtensionsMap->GetAllExtensions() as $sExtensionVersion => $oExtension) { - if (utils::StartsWith($sExtensionVersion, $aChoice['extension_code'].'/')) { - $aModuleInfo = $oExtension->aModuleInfo; - break; - } - } - if ((isset($aChoice['mandatory']) && $aChoice['mandatory']) || - (isset($aSelectedChoices[$sChoiceId]) && ($aSelectedChoices[$sChoiceId] == $sChoiceId))) { - $sDisplayChoices .= '
  • '.$aChoice['title'].'
  • '; - if (isset($aChoice['modules'])) { - foreach ($aChoice['modules'] as $sModuleId) { - $bSelected = true; - if (isset($aModuleInfo[$sModuleId])) { - // Test if module has 'auto_select' - $aInfo = $aModuleInfo[$sModuleId]; - if (isset($aInfo['auto_select'])) { - // Check the module selection - try { - SetupInfo::SetSelectedModules($aModules); - $bSelected = $this->GetPhpExpressionEvaluator()->ParseAndEvaluateBooleanExpression($aInfo['auto_select']); - } catch (ModuleFileReaderException $e) { - //logged already - $bSelected = false; - } - } - } - if ($bSelected) { - $aModules[$sModuleId] = true; // store the Id of the selected module - SetupInfo::SetSelectedModules($aModules); - } - } - } - $sChoiceType = isset($aChoice['type']) ? $aChoice['type'] : 'wizard_option'; - if ($aSelectedExtensions !== null) { - $aSelectedExtensions[] = $aChoice['extension_code']; - } - // Recurse only for selected choices - if (isset($aChoice['sub_options'])) { - $sDisplayChoices .= '
      '; - $sDisplayChoices = $this->GetSelectedModules($aChoice['sub_options'], $aSelectedChoices, $aModules, $sChoiceId, $sDisplayChoices, $aSelectedExtensions); - $sDisplayChoices .= '
    '; - } - $sDisplayChoices .= ''; - } - } - - $aAlternatives = isset($aInfo['alternatives']) ? $aInfo['alternatives'] : []; - $sChoiceName = null; - foreach ($aAlternatives as $index => $aChoice) { - $sChoiceId = $sParentId.self::$SEP.$index; - if ($sChoiceName == null) { - $sChoiceName = $sChoiceId; - } - if ((isset($aChoice['mandatory']) && $aChoice['mandatory']) || - (isset($aSelectedChoices[$sChoiceName]) && ($aSelectedChoices[$sChoiceName] == $sChoiceId))) { - $sDisplayChoices .= '
  • '.$aChoice['title'].'
  • '; - if ($aSelectedExtensions !== null) { - $aSelectedExtensions[] = $aChoice['extension_code']; - } - if (isset($aChoice['modules'])) { - foreach ($aChoice['modules'] as $sModuleId) { - $aModules[$sModuleId] = true; // store the Id of the selected module - } - } - // Recurse only for selected choices - if (isset($aChoice['sub_options'])) { - $sDisplayChoices .= '
      '; - $sDisplayChoices = $this->GetSelectedModules($aChoice['sub_options'], $aSelectedChoices, $aModules, $sChoiceId, $sDisplayChoices, $aSelectedExtensions); - $sDisplayChoices .= '
    '; - } - $sDisplayChoices .= ''; - } - } - if ($sParentId == '') { - // Last pass (after all the user's choices are turned into "selected" modules): - // Process 'auto_select' modules for modules that are not already selected - do { - // Loop while new modules are added... - $bModuleAdded = false; - foreach ($this->aAnalyzeInstallationModules as $sModuleId => $aModule) { - if (($sModuleId != ROOT_MODULE) && !array_key_exists($sModuleId, $aModules) && isset($aModule['auto_select'])) { - try { - SetupInfo::SetSelectedModules($aModules); - $bSelected = $this->GetPhpExpressionEvaluator()->ParseAndEvaluateBooleanExpression($aModule['auto_select']); - if ($bSelected) { - $aModules[$sModuleId] = true; // store the Id of the selected module - $sDisplayChoices .= '
  • '.$aModule['label'].' (auto_select)
  • '; - $bModuleAdded = true; - } - } catch (ModuleFileReaderException $e) { - //logged already - $sDisplayChoices .= '
  • Warning: auto_select failed with exception ('.$e->getMessage().') for module "'.$sModuleId.'"
  • '; - } - } - } - } while ($bModuleAdded); - } - - return $sDisplayChoices; - } - - protected function GetStepIndex() - { - switch ($this->sCurrentState) { - case 'start_install': - case 'start_upgrade': - $index = 0; - break; - - default: - $index = (int)$this->sCurrentState; - } - return $index; - } - - protected function GetStepInfo($idx = null) - { - $index = $idx ?? $this->GetStepIndex(); - - if (is_null($this->aSteps)) { - $this->oWizard->SetParameter('additional_extensions_modules', json_encode([])); // Default value, no additional extensions - - if (@file_exists($this->GetSourceFilePath())) { - // Found an "installation.xml" file, let's use this definition for the wizard - $aParams = new XMLParameters($this->GetSourceFilePath()); - $this->aSteps = $aParams->Get('steps', []); - - if ($index + 1 >= count($this->aSteps)) { - //make sure we also cache next step as well - $aOptions = $this->oExtensionsMap->GetAllExtensionsOptionInfo(); - - // Display this step of the wizard only if there is something to display - if (count($aOptions) > 0) { - $this->aSteps[] = [ - 'title' => 'Extensions', - 'description' => '

    Select additional extensions to install. You can launch the installation again to install new extensions or remove installed ones.

    ', - 'banner' => '/images/icons/icons8-puzzle.svg', - 'options' => $aOptions, - ]; - $this->oWizard->SetParameter('additional_extensions_modules', json_encode($aOptions)); - } - } - } else { - $aOptions = $this->oExtensionsMap->GetAllExtensionsOptionInfo(); - - // No wizard configuration provided, build a standard one with just one big list. All items are mandatory, only works when there are no conflicted modules. - $this->aSteps = [ - [ - 'title' => 'Modules Selection', - 'description' => '

    Select the modules to install. You can launch the installation again to install new modules, but you cannot remove already installed modules.

    ', - 'banner' => '/images/icons/icons8-apps-tab.svg', - 'options' => $aOptions, - ], - ]; - } - } -var_dump($this->aSteps[$index]); - return $this->aSteps[$index] ?? null; - } - - public function ComputeChoiceFlags(array $aChoice, string $sChoiceId, array $aSelectedComponents, bool $bAllDisabled, bool $bDisableUninstallCheck, bool $bUpgradeMode) - { - $oITopExtension = $this->oExtensionsMap->GetFromExtensionCode($aChoice['extension_code']); - $bCanBeUninstalled = isset($aChoice['uninstallable']) ? $aChoice['uninstallable'] === true || $aChoice['uninstallable'] === 'yes' : $oITopExtension->CanBeUninstalled(); - $bSelected = isset($aSelectedComponents[$sChoiceId]) && ($aSelectedComponents[$sChoiceId] == $sChoiceId); - $bMandatory = (isset($aChoice['mandatory']) && $aChoice['mandatory']) || $bUpgradeMode && $oITopExtension->bInstalled && !$bCanBeUninstalled && !$bDisableUninstallCheck; - - $bMissingFromDisk = isset($aChoice['missing']) && $aChoice['missing'] === true; - $bInstalled = $bMissingFromDisk || $oITopExtension->bInstalled; - $bDisabled = $bMandatory || $bAllDisabled || $bMissingFromDisk; - $bChecked = $bMandatory || $bSelected; - - if (isset($aChoice['sub_options'])) { - $aOptions = $aChoice['sub_options']['options'] ?? []; - foreach ($aOptions as $index => $aSubChoice) { - $sSubChoiceId = $sChoiceId.self::$SEP.$index; - $aSubFlags = $this->ComputeChoiceFlags($aSubChoice, $sSubChoiceId, $aSelectedComponents, $bAllDisabled, $bDisableUninstallCheck, $bUpgradeMode); - if ($aSubFlags['checked']) { - $bChecked = true; - if ($aSubFlags['disabled']) { - //If some sub options are enabled and cannot be disabled, this choice should also cannot be disabled since it would disable all its sub options - $bDisabled = true; - } - } - - } - } - - return [ - 'uninstallable' => $bCanBeUninstalled, - 'missing' => $bMissingFromDisk, - 'installed' => $bInstalled, - 'disabled' => $bDisabled, - 'checked' => $bChecked, - ]; - } - - protected function DisplayOptions($oPage, $aStepInfo, $aSelectedComponents, $aDefaults, $sParentId = '', $bAllDisabled = false) - { - $aOptions = $aStepInfo['options'] ?? []; - $aAlternatives = $aStepInfo['alternatives'] ?? []; - - $bDisableUninstallCheck = (bool)$this->oWizard->GetParameter('force-uninstall', false); - - foreach ($aOptions as $index => $aChoice) { - $sChoiceId = $sParentId.self::$SEP.$index; - $sDataId = 'data-id="'.utils::EscapeHtml($aChoice['extension_code']).'"'; - $sId = utils::EscapeHtml($aChoice['extension_code']); - $aFlags = static::ComputeChoiceFlags($aChoice, $sChoiceId, $aSelectedComponents, $bAllDisabled, $bDisableUninstallCheck, $this->bUpgrade); - - $sTooltip = ''; - $sUnremovable = ''; - if ($aFlags['missing']) { - $sTooltip .= '
    source removed
    '; - } - if ($aFlags['installed']) { - $sTooltip .= '
    installed
    '; - $sTooltip .= '
    to be uninstalled
    '; - } else { - $sTooltip .= '
    to be installed
    '; - $sTooltip .= '
    not installed
    '; - } - if (!$aFlags['uninstallable']) { - $sTooltip .= '
    cannot be uninstalled
    '; - } - if ($aFlags['disabled'] && !$aFlags['checked'] && !$aFlags['uninstallable'] && !$bDisableUninstallCheck) { - $this->bCanMoveForward = false;//Disable "Next" - } - $sChecked = $aFlags['checked'] ? ' checked ' : ''; - $sDisabled = $aFlags['disabled'] ? ' disabled data-disabled="disabled" ' : ''; - $sMissingModule = $aFlags['missing'] ? 'setup-extension--missing' : ''; - - $sHiddenInput = $aFlags['disabled'] && $aFlags['checked'] ? '' : ''; - $oPage->add('
    '.$sHiddenInput.' '); - $this->DisplayChoice($oPage, $aChoice, $aSelectedComponents, $aDefaults, $sChoiceId, $aFlags['disabled'], $sTooltip); - $oPage->add('
    '); - } - $sChoiceName = null; - $sDisabled = ''; - $bDisabled = false; - $sChoiceIdNone = null; - foreach ($aAlternatives as $index => $aChoice) { - $sChoiceId = $sParentId.self::$SEP.$index; - if ($sChoiceName == null) { - $sChoiceName = $sChoiceId; // All radios share the same name - } - $bIsDefault = array_key_exists($sChoiceName, $aDefaults) && ($aDefaults[$sChoiceName] == $sChoiceId); - $bMandatory = (isset($aChoice['mandatory']) && $aChoice['mandatory']) || ($this->bUpgrade && $bIsDefault); - if ($bMandatory || $bAllDisabled) { - // One choice is mandatory, all alternatives are disabled - $sDisabled = ' disabled data-disabled="disabled"'; - $bDisabled = true; - } - if ((!isset($aChoice['sub_options']) || (count($aChoice['sub_options']) == 0)) && (!isset($aChoice['modules']) || (count($aChoice['modules']) == 0))) { - $sChoiceIdNone = $sChoiceId; // the "None" / empty choice - } - } - - if (!array_key_exists($sChoiceName, $aDefaults) || ($aDefaults[$sChoiceName] == $sChoiceIdNone)) { - // The "none" choice does not disable the selection !! - $sDisabled = ''; - $bDisabled = false; - } - - foreach ($aAlternatives as $index => $aChoice) { - $sAttributes = ''; - $sChoiceId = $sParentId.self::$SEP.$index; - $sDataId = 'data-id="'.utils::EscapeHtml($aChoice['extension_code']).'"'; - $sId = utils::EscapeHtml($aChoice['extension_code']); - if ($sChoiceName == null) { - $sChoiceName = $sChoiceId; // All radios share the same name - } - $bIsDefault = array_key_exists($sChoiceName, $aDefaults) && ($aDefaults[$sChoiceName] == $sChoiceId); - $bSelected = isset($aSelectedComponents[$sChoiceName]) && ($aSelectedComponents[$sChoiceName] == $sChoiceId); - if (!isset($aSelectedComponents[$sChoiceName]) && ($sChoiceIdNone != null)) { - // No choice selected, select the "None" option - $bSelected = ($sChoiceId == $sChoiceIdNone); - } - $bMandatory = (isset($aChoice['mandatory']) && $aChoice['mandatory']) || ($this->bUpgrade && $bIsDefault); - - if ($bSelected) { - $sAttributes = ' checked '; - } - $sHidden = ''; - if ($bMandatory && $bDisabled) { - $sAttributes = ' checked '; - $sHidden = ''; - } - $oPage->add('
    '.$sHidden.' '); - $this->DisplayChoice($oPage, $aChoice, $aSelectedComponents, $aDefaults, $sChoiceId, $bDisabled && !$bSelected); - $oPage->add('
    '); - } - } - - protected function DisplayChoice($oPage, $aChoice, $aSelectedComponents, $aDefaults, $sChoiceId, $bDisabled = false, $sTooltip = '') - { - $sMoreInfo = (isset($aChoice['more_info']) && ($aChoice['more_info'] != '')) ? 'More information' : ''; - $sSourceLabel = $aChoice['source_label'] ?? ''; - $sId = utils::EscapeHtml($aChoice['extension_code']); - - $oPage->add(' '.$sMoreInfo.''); - $sDescription = isset($aChoice['description']) ? utils::EscapeHtml($aChoice['description']) : ''; - $oPage->add('
    '.$sDescription.''); - if (isset($aChoice['sub_options'])) { - $this->DisplayOptions($oPage, $aChoice['sub_options'], $aSelectedComponents, $aDefaults, $sChoiceId, $bDisabled); - } - $oPage->add('
    '); - } - - protected function GetSourceFilePath() - { - $sSourceDir = $this->oWizard->GetParameter('source_dir'); - return $sSourceDir.'/installation.xml'; - } - - public function CanMoveForward() - { - return true; - } - - public function JSCanMoveForward() - { - - return $this->bCanMoveForward ? 'return true;' : 'return false;'; - } - - public function GetNextButtonLabel() - { - return $this->bCanMoveForward ? 'Next' : 'Non-uninstallable extension missing'; - } - -} - -class WizStepAudit extends WizardStep -{ - public function GetTitle() - { - return 'Checking upgrade'; - - } - - public function GetPossibleSteps() - { - return ['WizStepSummary']; - } - - public function GetNextButtonLabel() - { - return 'Next'; - } - - public function CanMoveForward() - { - return true; - - } - - public function ProcessParams($bMoveForward = true) - { - return ['class' => 'WizStepSummary', 'state' => '']; - } - - public function Display(WebPage $oPage) - { - $oPage->add('

    Progress bar

    '); - } - - public function AsyncAction(WebPage $oPage, $sCode, $aParameters) - { - $oParameters = new PHPParameters(); - $sStep = $aParameters['installer_step']; - $sJSONParameters = $aParameters['installer_config']; - $oParameters->LoadFromHash(json_decode($sJSONParameters, true /* bAssoc */)); - $oInstaller = new ApplicationInstaller($oParameters); - $aRes = $oInstaller->ExecuteStep($sStep); - if (($aRes['status'] != ApplicationInstaller::ERROR) && ($aRes['next-step'] != '')) { - // Tell the web page to move the progress bar and to launch the next step - $sMessage = addslashes(utils::EscapeHtml($aRes['next-step-label'])); - $oPage->add_ready_script( - <<{$aRes['next-step-label']}'); - ExecuteStep('{$aRes['next-step']}'); -EOF - ); - } elseif ($aRes['status'] != ApplicationInstaller::ERROR) { - // Installation complete, move to the next step of the wizard - $oPage->add_ready_script( - <<', $sMessage); - $oPage->add_ready_script( - <<bDependencyCheck)) { - $aSelectedModules = json_decode($this->oWizard->GetParameter('selected_modules'), true); - $this->bDependencyCheck = true; - try { - SetupUtils::AnalyzeInstallation($this->oWizard, true, $aSelectedModules); - } catch (MissingDependencyException $e) { - $this->bDependencyCheck = false; - $this->sDependencyIssue = $e->getHtmlDesc(); - } - } - return $this->bDependencyCheck; - } - - public function GetTitle() - { - $sMode = $this->oWizard->GetParameter('mode', 'install'); - if ($sMode == 'install') { - return 'Ready to install'; - - } else { - return 'Ready to upgrade'; - } - } - - public function GetPossibleSteps() - { - return ['WizStepBuild']; - } - - /** - * Returns the label for the " Next >> " button - * @return string The label for the button - */ - public function GetNextButtonLabel() - { - return 'Install'; - } - - public function CanMoveForward() - { - if ($this->CheckDependencies()) { - return true; - } else { - return false; - } - } - - public function ProcessParams($bMoveForward = true) - { - return ['class' => 'WizStepBuild', 'state' => '']; - } - - public function Display(WebPage $oPage) - { - - $aInstallParams = $this->BuildConfig(); - - $sMode = $aInstallParams['mode']; - - $sDestination = ITOP_APPLICATION.(($sMode == 'install') ? ' version '.ITOP_VERSION.' is about to be installed ' : ' is about to be upgraded '); - $sDBDescription = ' existing database '.$aInstallParams['database']['name'].''; - if (($sMode == 'install') && ($this->oWizard->GetParameter('create_db') == 'yes')) { - $sDBDescription = ' new database '.$aInstallParams['database']['name'].''; - } - $sDestination .= 'into the '.$sDBDescription.' on the server '.$aInstallParams['database']['server'].'.'; - $oPage->add('

    '.$sDestination.'

    '); - - $oPage->add('
    Installation Parameters'); - $oPage->add('
    '); - - $oPage->add('
    Extensions to be installed'); - $aExtensionsAdded = json_decode($this->oWizard->GetParameter('extensions_added'), true); - - if (count($aExtensionsAdded) > 0) { - $sExtensionsAdded = '
      '; - foreach ($aExtensionsAdded as $sExtensionCode => $sLabel) { - $sExtensionsAdded .= "
    • $sLabel
    • '"; - } - $sExtensionsAdded .= '
    '; - } else { - $sExtensionsAdded = '
    • No extension added.
    '; - } - $oPage->add($sExtensionsAdded); - $oPage->add('
    '); - $oPage->add('
    Extensions to be uninstalled'); - - $aExtensionsRemoved = json_decode($this->oWizard->GetParameter('removed_extensions'), true) ?? []; - $aExtensionsNotUninstallable = json_decode($this->oWizard->GetParameter('extensions_not_uninstallable')); - if (count($aExtensionsRemoved) > 0) { - $sExtensionsRemoved = '
      '; - foreach ($aExtensionsRemoved as $sExtensionCode => $sLabel) { - if (in_array($sExtensionCode, $aExtensionsNotUninstallable)) { - $sExtensionsRemoved .= "
    • $sLabel (forced uninstallation)
    • "; - } else { - $sExtensionsRemoved .= "
    • $sLabel
    • "; - } - } - $sExtensionsRemoved .= '
    '; - } else { - $sExtensionsRemoved = '
    • No extension removed.
    '; - } - $oPage->add($sExtensionsRemoved); - $oPage->add('
    '); - - $oPage->add('
    Database Parameters
      '); - $oPage->add('
    • Server Name: '.$aInstallParams['database']['server'].'
    • '); - $oPage->add('
    • DB User Name: '.$aInstallParams['database']['user'].'
    • '); - $oPage->add('
    • DB user password: ***
    • '); - if (($sMode == 'install') && ($this->oWizard->GetParameter('create_db') == 'yes')) { - $oPage->add('
    • Database Name: '.$aInstallParams['database']['name'].' (will be created)
    • '); - } else { - $oPage->add('
    • Database Name: '.$aInstallParams['database']['name'].'
    • '); - } - if ($aInstallParams['database']['prefix'] != '') { - $oPage->add('
    • Prefix for the '.ITOP_APPLICATION.' tables: '.$aInstallParams['database']['prefix'].'
    • '); - } else { - $oPage->add('
    • Prefix for the '.ITOP_APPLICATION.' tables: none
    • '); - } - $oPage->add('
    '); - - $oPage->add('
    Data Model Configuration'); - $oPage->add($this->oWizard->GetParameter('display_choices')); - $oPage->add('
    '); - - $oPage->add('
    Other Parameters
      '); - if ($sMode == 'install') { - $oPage->add('
    • Default language: '.$aInstallParams['language'].'
    • '); - } - - $oPage->add('
    • URL to access the application: '.$aInstallParams['url'].'
    • '); - $oPage->add('
    • Graphviz\' dot path: '.$aInstallParams['graphviz_path'].'
    • '); - if ($aInstallParams['sample_data'] == 'yes') { - $oPage->add('
    • Sample data will be loaded into the database.
    • '); - } - if ($aInstallParams['old_addon']) { - $oPage->add('
    • Compatibility mode: Using the version 1.2 of the UserRightsProfiles add-on.
    • '); - } - $oPage->add('
    '); - - if ($sMode == 'install') { - $oPage->add('
    Admininistrator Account
      '); - $oPage->add('
    • Login: '.$aInstallParams['admin_account']['user'].'
    • '); - $oPage->add('
    • Password: '.$aInstallParams['admin_account']['pwd'].'
    • '); - $oPage->add('
    • Language: '.$aInstallParams['admin_account']['language'].'
    • '); - $oPage->add('
    '); - } - - $aMiscOptions = $aInstallParams['options']; - if (count($aMiscOptions) > 0) { - $oPage->add('
    Miscellaneous Options
      '); - foreach ($aMiscOptions as $sKey => $sValue) { - $oPage->add('
    • '.$sKey.': '.$sValue.'
    • '); - } - $oPage->add('
    '); - - } - - if (isset($aMiscOptions['generate_config'])) { - $oDoc = new DOMDocument('1.0', 'UTF-8'); - $oDoc->preserveWhiteSpace = false; - $oDoc->formatOutput = true; - $oParams = new PHPParameters(); - $oParams->LoadFromHash($aInstallParams); - $oParams->ToXML($oDoc, null, 'installation'); - $sXML = $oDoc->saveXML(); - $oPage->add('
    XML Config file
      ');
      -			$oPage->add(utils::EscapeHtml($sXML));
      -			$oPage->add('
    '); - } - - $oPage->add('
    '); // params_summary - $oPage->add('
    '); - - if (!$this->CheckDependencies()) { - $oPage->error($this->sDependencyIssue); - } - - $oPage->add_ready_script( - <<oWizard->GetParameter('install_mode', 'install'); - $aSelectedModules = json_decode($this->oWizard->GetParameter('selected_modules'), true); - $aSelectedExtensions = json_decode($this->oWizard->GetParameter('selected_extensions'), true); - $sBackupDestination = ''; - $sPreviousConfigurationFile = ''; - $sDBName = $this->oWizard->GetParameter('db_name'); - if ($sMode == 'upgrade') { - $sPreviousVersionDir = $this->oWizard->GetParameter('previous_version_dir', ''); - if (!empty($sPreviousVersionDir)) { - $aPreviousInstance = SetupUtils::GetPreviousInstance($sPreviousVersionDir); - if ($aPreviousInstance['found']) { - $sPreviousConfigurationFile = $aPreviousInstance['configuration_file']; - } - } - - if ($this->oWizard->GetParameter('db_backup', false)) { - $sBackupDestination = $this->oWizard->GetParameter('db_backup_path', ''); - } - } else { - - $sDBNewName = $this->oWizard->GetParameter('db_new_name', ''); - if ($sDBNewName != '') { - $sDBName = $sDBNewName; // Database will be created - } - } - - $sSourceDir = $this->oWizard->GetParameter('source_dir'); - $aCopies = []; - 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'; - } - - $aInstallParams = [ - 'mode' => $sMode, - 'preinstall' => [ - 'copies' => $aCopies, - // 'backup' => see below - ], - 'source_dir' => str_replace(APPROOT, '', $sSourceDir), - 'datamodel_version' => $this->oWizard->GetParameter('datamodel_version'), //TODO: let the installer compute this automatically... - 'previous_configuration_file' => $sPreviousConfigurationFile, - 'extensions_dir' => 'extensions', - 'target_env' => 'production', - 'workspace_dir' => '', - 'database' => [ - 'server' => $this->oWizard->GetParameter('db_server'), - 'user' => $this->oWizard->GetParameter('db_user'), - 'pwd' => $this->oWizard->GetParameter('db_pwd'), - 'name' => $sDBName, - 'db_tls_enabled' => $this->oWizard->GetParameter('db_tls_enabled'), - 'db_tls_ca' => $this->oWizard->GetParameter('db_tls_ca'), - 'prefix' => $this->oWizard->GetParameter('db_prefix'), - ], - 'url' => $this->oWizard->GetParameter('application_url'), - 'graphviz_path' => $this->oWizard->GetParameter('graphviz_path'), - 'admin_account' => [ - 'user' => $this->oWizard->GetParameter('admin_user'), - 'pwd' => $this->oWizard->GetParameter('admin_pwd'), - 'language' => $this->oWizard->GetParameter('admin_language'), - ], - 'language' => $this->oWizard->GetParameter('default_language'), - 'selected_modules' => $aSelectedModules, - 'selected_extensions' => $aSelectedExtensions, - 'sample_data' => ($this->oWizard->GetParameter('sample_data', '') == 'yes') ? true : false , - 'old_addon' => $this->oWizard->GetParameter('old_addon', false), // whether or not to use the "old" userrights profile addon - 'options' => json_decode($this->oWizard->GetParameter('misc_options', '[]'), true), - 'mysql_bindir' => $this->oWizard->GetParameter('mysql_bindir'), - ]; - - if ($sBackupDestination != '') { - $aInstallParams['preinstall']['backup'] = [ - 'destination' => $sBackupDestination, - 'configuration_file' => $sPreviousConfigurationFile, - ]; - } - - return $aInstallParams; - } - - public function AsyncAction(WebPage $oPage, $sCode, $aParameters) - { - $oParameters = new PHPParameters(); - $sStep = $aParameters['installer_step']; - $sJSONParameters = $aParameters['installer_config']; - $oParameters->LoadFromHash(json_decode($sJSONParameters, true /* bAssoc */)); - $oInstaller = new ApplicationInstaller($oParameters); - $aRes = $oInstaller->ExecuteStep($sStep); - if (($aRes['status'] != ApplicationInstaller::ERROR) && ($aRes['next-step'] != '')) { - // Tell the web page to move the progress bar and to launch the next step - $sMessage = addslashes(utils::EscapeHtml($aRes['next-step-label'])); - $oPage->add_ready_script( - <<{$aRes['next-step-label']}'); - ExecuteStep('{$aRes['next-step']}'); -EOF - ); - } elseif ($aRes['status'] != ApplicationInstaller::ERROR) { - // Installation complete, move to the next step of the wizard - $oPage->add_ready_script( - <<', $sMessage); - $oPage->add_ready_script( - <<bDependencyCheck)) { - $aSelectedModules = json_decode($this->oWizard->GetParameter('selected_modules'), true); - $this->bDependencyCheck = true; - try { - SetupUtils::AnalyzeInstallation($this->oWizard, true, $aSelectedModules); - } catch (MissingDependencyException $e) { - $this->bDependencyCheck = false; - $this->sDependencyIssue = $e->getHtmlDesc(); - } - } - return $this->bDependencyCheck; - } - - public function GetTitle() - { - return 'Building iTop'; - } - - public function GetPossibleSteps() - { - return ['WizStepDone']; - } - - /** - * Returns the label for the " Next >> " button - * @return string The label for the button - */ - public function GetNextButtonLabel() - { - return 'Install'; - } - - public function CanMoveForward() - { - if ($this->CheckDependencies()) { - return true; - } else { - return false; - } - } - - public function ProcessParams($bMoveForward = true) - { - return ['class' => 'WizStepDone', 'state' => '']; - } - - public function Display(WebPage $oPage) - { - - $aInstallParams = $this->BuildConfig(); - - $oPage->add('
    Progress of the installation'); - $oPage->add('
    '); - $oPage->LinkScriptFromAppRoot('setup/jquery.progression.js'); - $oPage->add('

    Ready to start...

    0%
    '); - $oPage->add('
    '); // progress_content - $oPage->add('
    '); - - $sJSONData = json_encode($aInstallParams); - $oPage->add(''); - - $sAuthentToken = $this->oWizard->GetParameter('authent', ''); - $oPage->add(''); - - if (!$this->CheckDependencies()) { - $oPage->error($this->sDependencyIssue); - } - - $oPage->add_ready_script( - <<oWizard->GetParameter('install_mode', 'install'); - $aSelectedModules = json_decode($this->oWizard->GetParameter('selected_modules'), true); - $aSelectedExtensions = json_decode($this->oWizard->GetParameter('selected_extensions'), true); - $sBackupDestination = ''; - $sPreviousConfigurationFile = ''; - $sDBName = $this->oWizard->GetParameter('db_name'); - if ($sMode == 'upgrade') { - $sPreviousVersionDir = $this->oWizard->GetParameter('previous_version_dir', ''); - if (!empty($sPreviousVersionDir)) { - $aPreviousInstance = SetupUtils::GetPreviousInstance($sPreviousVersionDir); - if ($aPreviousInstance['found']) { - $sPreviousConfigurationFile = $aPreviousInstance['configuration_file']; - } - } - - if ($this->oWizard->GetParameter('db_backup', false)) { - $sBackupDestination = $this->oWizard->GetParameter('db_backup_path', ''); - } - } else { - - $sDBNewName = $this->oWizard->GetParameter('db_new_name', ''); - if ($sDBNewName != '') { - $sDBName = $sDBNewName; // Database will be created - } - } - - $sSourceDir = $this->oWizard->GetParameter('source_dir'); - $aCopies = []; - 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'; - } - - $aInstallParams = [ - 'mode' => $sMode, - 'preinstall' => [ - 'copies' => $aCopies, - // 'backup' => see below - ], - 'source_dir' => str_replace(APPROOT, '', $sSourceDir), - 'datamodel_version' => $this->oWizard->GetParameter('datamodel_version'), //TODO: let the installer compute this automatically... - 'previous_configuration_file' => $sPreviousConfigurationFile, - 'extensions_dir' => 'extensions', - 'target_env' => 'production', - 'workspace_dir' => '', - 'database' => [ - 'server' => $this->oWizard->GetParameter('db_server'), - 'user' => $this->oWizard->GetParameter('db_user'), - 'pwd' => $this->oWizard->GetParameter('db_pwd'), - 'name' => $sDBName, - 'db_tls_enabled' => $this->oWizard->GetParameter('db_tls_enabled'), - 'db_tls_ca' => $this->oWizard->GetParameter('db_tls_ca'), - 'prefix' => $this->oWizard->GetParameter('db_prefix'), - ], - 'url' => $this->oWizard->GetParameter('application_url'), - 'graphviz_path' => $this->oWizard->GetParameter('graphviz_path'), - 'admin_account' => [ - 'user' => $this->oWizard->GetParameter('admin_user'), - 'pwd' => $this->oWizard->GetParameter('admin_pwd'), - 'language' => $this->oWizard->GetParameter('admin_language'), - ], - 'language' => $this->oWizard->GetParameter('default_language'), - 'selected_modules' => $aSelectedModules, - 'selected_extensions' => $aSelectedExtensions, - 'sample_data' => ($this->oWizard->GetParameter('sample_data', '') == 'yes') ? true : false , - 'old_addon' => $this->oWizard->GetParameter('old_addon', false), // whether or not to use the "old" userrights profile addon - 'options' => json_decode($this->oWizard->GetParameter('misc_options', '[]'), true), - 'mysql_bindir' => $this->oWizard->GetParameter('mysql_bindir'), - ]; - - if ($sBackupDestination != '') { - $aInstallParams['preinstall']['backup'] = [ - 'destination' => $sBackupDestination, - 'configuration_file' => $sPreviousConfigurationFile, - ]; - } - - return $aInstallParams; - } - - public function AsyncAction(WebPage $oPage, $sCode, $aParameters) - { - $oParameters = new PHPParameters(); - $sStep = $aParameters['installer_step']; - $sJSONParameters = $aParameters['installer_config']; - $oParameters->LoadFromHash(json_decode($sJSONParameters, true /* bAssoc */)); - $oInstaller = new ApplicationInstaller($oParameters); - $aRes = $oInstaller->ExecuteStep($sStep); - if (($aRes['status'] != ApplicationInstaller::ERROR) && ($aRes['next-step'] != '')) { - // Tell the web page to move the progress bar and to launch the next step - $sMessage = addslashes(utils::EscapeHtml($aRes['next-step-label'])); - $oPage->add_ready_script( - <<{$aRes['next-step-label']}'); - ExecuteStep('{$aRes['next-step']}'); -EOF - ); - } elseif ($aRes['status'] != ApplicationInstaller::ERROR) { - // Installation complete, move to the next step of the wizard - $oPage->add_ready_script( - <<', $sMessage); - $oPage->add_ready_script( - << '', 'state' => '']; - } - - public function Display(WebPage $oPage) - { - // Check if there are some manual steps required: - $aManualSteps = []; - $aAvailableModules = SetupUtils::AnalyzeInstallation($this->oWizard); - - $sRootUrl = utils::GetAbsoluteUrlAppRoot(true); - $aSelectedModules = json_decode($this->oWizard->GetParameter('selected_modules'), true); - foreach ($aSelectedModules as $sModuleId) { - if (!empty($aAvailableModules[$sModuleId]['doc.manual_setup'])) { - $sUrl = $aAvailableModules[$sModuleId]['doc.manual_setup']; - $sManualStepUrl = utils::IsURL($sUrl) ? $sUrl : $sRootUrl.$sUrl; - $aManualSteps[$aAvailableModules[$sModuleId]['label']] = $sManualStepUrl; - } - } - $oPage->add('
    '); - if (count($aManualSteps) > 0) { - $oPage->add("

    Manual operations required

    "); - $oPage->p("In order to complete the installation, the following manual operations are required:"); - foreach ($aManualSteps as $sModuleLabel => $sUrl) { - $oPage->p("Manual instructions for $sModuleLabel"); - } - $oPage->add("

    Congratulations for installing ".ITOP_APPLICATION."

    "); - } else { - $oPage->add("

    Congratulations for installing ".ITOP_APPLICATION."

    "); - $oPage->ok("The installation completed successfully."); - } - - $bHasBackup = false; - if (($this->oWizard->GetParameter('mode', '') == 'upgrade') && $this->oWizard->GetParameter('db_backup', false) && $this->oWizard->GetParameter('authent', false)) { - $sBackupDestination = $this->oWizard->GetParameter('db_backup_path', ''); - if (file_exists($sBackupDestination.'.tar.gz')) { - $bHasBackup = true; - // To mitigate security risks: pass only the filename without the extension, the download will add the extension itself - $oPage->p('Your backup is ready'); - $oPage->p(' Download '.basename($sBackupDestination).''); - } else { - $oPage->p(' Warning: Backup creation failed !'); - } - } - - // Form goes here.. No back button since the job is done ! - $oPage->add(''); - - $oPage->add('
    '); - - $oConfig = new Config(utils::GetConfigFilePath()); - $aParamValues = $this->oWizard->GetParamForConfigArray(); - $oConfig->UpdateFromParams($aParamValues); - // Load the data model only, in order to load env-production/core/main.php to get the XML parameters (needed by GetModuleSettings below) - // But main.php may also contain classes (defined without any module), and thus requiring the full data model - // to be loaded to prevent "class not found" errors... - $oProductionEnv = new RunTimeEnvironment('production'); - $oProductionEnv->InitDataModel($oConfig, true); - $sIframeUrl = $oConfig->GetModuleSetting('itop-hub-connector', 'setup_url', ''); - - $sSetupTokenFile = APPROOT.'data/.setup'; - $sSetupToken = bin2hex(random_bytes(12)); - file_put_contents($sSetupTokenFile, $sSetupToken); - $sIframeUrl .= "&setup_token=$sSetupToken"; - - if ($sIframeUrl != '') { - $oPage->add(''); - - $oPage->add_script(" - window.addEventListener('message', function(event) { - if (event.data === 'itophub_load_completed') - { - $('#placeholder').hide(); - $('#fresh_content').show(); - } - }, false); - "); - } - - $sForm = '
    '; - $sForm .= ''; - $sForm .= ''; - $sForm .= "
    "; - $sForm .= ''; - - $sForm = addslashes($sForm); - $oPage->add_ready_script("$('#wiz_form').append('$sForm');"); - // avoid leaving in a dirty state - SetupUtils::ExitMaintenanceMode(false); - SetupUtils::ExitReadOnlyMode(false); - - if (false === $bHasBackup) { - SetupUtils::EraseSetupToken(); - } - } - - public function CanMoveForward() - { - return false; - } - public function CanMoveBackward() - { - return false; - } - - /** - * 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 false; //This step executes once the config was written and secured - } - - public function AsyncAction(WebPage $oPage, $sCode, $aParameters) - { - SetupUtils::EraseSetupToken(); - // 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)) { - // Make sure there is NO output at all before our content, otherwise the document will be corrupted - $sPreviousContent = ob_get_clean(); - $oPage->SetContentType('application/gzip'); - $oPage->SetContentDisposition('attachment', basename($sBackupFile)); - $oPage->add(file_get_contents($sBackupFile)); - } - } -} diff --git a/setup/wizardsteps/AbstractWizStepBuild.php b/setup/wizardsteps/AbstractWizStepBuild.php new file mode 100644 index 000000000..cb8897e27 --- /dev/null +++ b/setup/wizardsteps/AbstractWizStepBuild.php @@ -0,0 +1,92 @@ +oWizard->GetParameter('install_mode', 'install'); + $aSelectedModules = json_decode($this->oWizard->GetParameter('selected_modules'), true); + $aSelectedExtensions = json_decode($this->oWizard->GetParameter('selected_extensions'), true); + $sBackupDestination = ''; + $sPreviousConfigurationFile = ''; + $sDBName = $this->oWizard->GetParameter('db_name'); + if ($sMode == 'upgrade') { + $sPreviousVersionDir = $this->oWizard->GetParameter('previous_version_dir', ''); + if (!empty($sPreviousVersionDir)) { + $aPreviousInstance = SetupUtils::GetPreviousInstance($sPreviousVersionDir); + if ($aPreviousInstance['found']) { + $sPreviousConfigurationFile = $aPreviousInstance['configuration_file']; + } + } + + if ($this->oWizard->GetParameter('db_backup', false)) { + $sBackupDestination = $this->oWizard->GetParameter('db_backup_path', ''); + } + } else { + + $sDBNewName = $this->oWizard->GetParameter('db_new_name', ''); + if ($sDBNewName != '') { + $sDBName = $sDBNewName; // Database will be created + } + } + + $sSourceDir = $this->oWizard->GetParameter('source_dir'); + $aCopies = []; + 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'; + } + + $aInstallParams = [ + 'mode' => $sMode, + 'preinstall' => [ + 'copies' => $aCopies, + // 'backup' => see below + ], + 'source_dir' => str_replace(APPROOT, '', $sSourceDir), + 'datamodel_version' => $this->oWizard->GetParameter('datamodel_version'), //TODO: let the installer compute this automatically... + 'previous_configuration_file' => $sPreviousConfigurationFile, + 'extensions_dir' => 'extensions', + 'target_env' => 'production', + 'workspace_dir' => '', + 'database' => [ + 'server' => $this->oWizard->GetParameter('db_server'), + 'user' => $this->oWizard->GetParameter('db_user'), + 'pwd' => $this->oWizard->GetParameter('db_pwd'), + 'name' => $sDBName, + 'db_tls_enabled' => $this->oWizard->GetParameter('db_tls_enabled'), + 'db_tls_ca' => $this->oWizard->GetParameter('db_tls_ca'), + 'prefix' => $this->oWizard->GetParameter('db_prefix'), + ], + 'url' => $this->oWizard->GetParameter('application_url'), + 'graphviz_path' => $this->oWizard->GetParameter('graphviz_path'), + 'admin_account' => [ + 'user' => $this->oWizard->GetParameter('admin_user'), + 'pwd' => $this->oWizard->GetParameter('admin_pwd'), + 'language' => $this->oWizard->GetParameter('admin_language'), + ], + 'language' => $this->oWizard->GetParameter('default_language'), + 'selected_modules' => $aSelectedModules, + 'selected_extensions' => $aSelectedExtensions, + 'sample_data' => ($this->oWizard->GetParameter('sample_data', '') == 'yes') ? true : false , + 'old_addon' => $this->oWizard->GetParameter('old_addon', false), // whether or not to use the "old" userrights profile addon + 'options' => json_decode($this->oWizard->GetParameter('misc_options', '[]'), true), + 'mysql_bindir' => $this->oWizard->GetParameter('mysql_bindir'), + ]; + + if ($sBackupDestination != '') { + $aInstallParams['preinstall']['backup'] = [ + 'destination' => $sBackupDestination, + 'configuration_file' => $sPreviousConfigurationFile, + ]; + } + + return $aInstallParams; + } + +} \ No newline at end of file diff --git a/setup/wizardsteps/AbstractWizStepMiscParams.php b/setup/wizardsteps/AbstractWizStepMiscParams.php new file mode 100644 index 000000000..8e6b379c4 --- /dev/null +++ b/setup/wizardsteps/AbstractWizStepMiscParams.php @@ -0,0 +1,72 @@ +add('
    '); + $oPage->add('Dev parameters'); + $oPage->p('
    '); + $oPage->add_ready_script( + <<<'JS' +$("#use-symbolic-links").on("click", function() { + var $this = $(this), + bUseSymbolicLinks = $this.prop("checked"); + var sAuthent = $('#authent_token').val(); + var oAjaxParams = { operation: 'toggle_use_symbolic_links', bUseSymbolicLinks: bUseSymbolicLinks, authent: sAuthent}; + $.post(GetAbsoluteUrlAppRoot()+'setup/ajax.dataloader.php', oAjaxParams); +}); +JS + ); + } + } + + final protected function AddForceUninstallFlagOption(WebPage $oPage): void + { + $sChecked = $this->oWizard->GetParameter('force-uninstall', false) ? ' checked ' : ''; + $oPage->add('
    '); + $oPage->add('Advanced parameters'); + $oPage->p('
    '); + + $oPage->add_ready_script( + <<<'JS' +$("#force-uninstall").on("click", function() { + let $this = $(this); + let bForceUninstall = $this.prop("checked"); + if( bForceUninstall && !confirm('Beware, uninstalling extensions flagged as non uninstallable may result in data corruption and application crashes. Are you sure you want to continue ?')){ + $this.prop("checked",false); + } +}); +JS + ); + } +} diff --git a/setup/wizardsteps/WizStepAdminAccount.php b/setup/wizardsteps/WizStepAdminAccount.php new file mode 100644 index 000000000..b8365daa9 --- /dev/null +++ b/setup/wizardsteps/WizStepAdminAccount.php @@ -0,0 +1,108 @@ +oWizard->SaveParameter('admin_user', ''); + $this->oWizard->SaveParameter('admin_pwd', ''); + $this->oWizard->SaveParameter('confirm_pwd', ''); + $this->oWizard->SaveParameter('admin_language', 'EN US'); + + return ['class' => WizStepInstallMiscParams::class, 'state' => '']; + } + + public function Display(WebPage $oPage) + { + $sAdminUser = $this->oWizard->GetParameter('admin_user', 'admin'); + $sAdminPwd = $this->oWizard->GetParameter('admin_pwd', ''); + $sConfirmPwd = $this->oWizard->GetParameter('confirm_pwd', ''); + $sAdminLanguage = $this->oWizard->GetParameter('admin_language', 'EN US'); + $oPage->add('

    Definition of the Administrator Account

    '); + $oPage->add('
    '); + $oPage->add('Administrator Account'); + $oPage->add(''); + $oPage->add(''); + $oPage->add(''); + $oPage->add(''); + $sSourceDir = APPROOT.'dictionaries/'; + $aLanguages = SetupUtils::GetAvailableLanguages($sSourceDir); + $oPage->add(''); + $oPage->add('
    Login:
    Password:
    Confirm password:
    Language: '); + $oPage->add(SetupUtils::GetLanguageSelect($sSourceDir, 'admin_language', $sAdminLanguage)); + $oPage->add('
    '); + $oPage->add('
    '); + $oPage->add_ready_script( + <<'); + } + else + { + $("#v_admin_user").html(''); + } + + bPasswordsMatch = ($('#admin_pwd').val() == $('#confirm_pwd').val()); + if (!bPasswordsMatch) + { + $('#v_admin_pwd').html(''); + } + else + { + $('#v_admin_pwd').html(''); + } + bRet = bPasswordsMatch && bRet; + + return bRet; +EOF + ; + } +} diff --git a/setup/wizardsteps/WizStepAudit.php b/setup/wizardsteps/WizStepAudit.php new file mode 100644 index 000000000..5bd782f4b --- /dev/null +++ b/setup/wizardsteps/WizStepAudit.php @@ -0,0 +1,120 @@ + 'WizStepSummary', 'state' => '']; + } + + public function Display(WebPage $oPage) + { + $oPage->add('

    Progress bar

    '); + } + + public function AsyncAction(WebPage $oPage, $sCode, $aParameters) + { + $oParameters = new PHPParameters(); + $sStep = $aParameters['installer_step']; + $sJSONParameters = $aParameters['installer_config']; + $oParameters->LoadFromHash(json_decode($sJSONParameters, true /* bAssoc */)); + $oInstaller = new ApplicationInstaller($oParameters); + $aRes = $oInstaller->ExecuteStep($sStep); + if (($aRes['status'] != ApplicationInstaller::ERROR) && ($aRes['next-step'] != '')) { + // Tell the web page to move the progress bar and to launch the next step + $sMessage = addslashes(utils::EscapeHtml($aRes['next-step-label'])); + $oPage->add_ready_script( + <<{$aRes['next-step-label']}'); + ExecuteStep('{$aRes['next-step']}'); +EOF + ); + } elseif ($aRes['status'] != ApplicationInstaller::ERROR) { + // Installation complete, move to the next step of the wizard + $oPage->add_ready_script( + <<', $sMessage); + $oPage->add_ready_script( + <<bDependencyCheck)) { + $aSelectedModules = json_decode($this->oWizard->GetParameter('selected_modules'), true); + $this->bDependencyCheck = true; + try { + SetupUtils::AnalyzeInstallation($this->oWizard, true, $aSelectedModules); + } catch (MissingDependencyException $e) { + $this->bDependencyCheck = false; + $this->sDependencyIssue = $e->getHtmlDesc(); + } + } + return $this->bDependencyCheck; + } + + public function GetTitle() + { + return 'Building iTop'; + } + + public function GetPossibleSteps() + { + return ['WizStepDone']; + } + + /** + * Returns the label for the " Next >> " button + * @return string The label for the button + */ + public function GetNextButtonLabel() + { + return 'Install'; + } + + public function CanMoveForward() + { + if ($this->CheckDependencies()) { + return true; + } else { + return false; + } + } + + public function ProcessParams($bMoveForward = true) + { + return ['class' => 'WizStepDone', 'state' => '']; + } + + public function Display(WebPage $oPage) + { + + $aInstallParams = $this->BuildConfig(); + + $oPage->add('
    Progress of the installation'); + $oPage->add('
    '); + $oPage->LinkScriptFromAppRoot('setup/jquery.progression.js'); + $oPage->add('

    Ready to start...

    0%
    '); + $oPage->add('
    '); // progress_content + $oPage->add('
    '); + + $sJSONData = json_encode($aInstallParams); + $oPage->add(''); + + $sAuthentToken = $this->oWizard->GetParameter('authent', ''); + $oPage->add(''); + + if (!$this->CheckDependencies()) { + $oPage->error($this->sDependencyIssue); + } + + $oPage->add_ready_script( + <<LoadFromHash(json_decode($sJSONParameters, true /* bAssoc */)); + $oInstaller = new ApplicationInstaller($oParameters); + $aRes = $oInstaller->ExecuteStep($sStep); + if (($aRes['status'] != ApplicationInstaller::ERROR) && ($aRes['next-step'] != '')) { + // Tell the web page to move the progress bar and to launch the next step + $sMessage = addslashes(utils::EscapeHtml($aRes['next-step-label'])); + $oPage->add_ready_script( + <<{$aRes['next-step-label']}'); + ExecuteStep('{$aRes['next-step']}'); +EOF + ); + } elseif ($aRes['status'] != ApplicationInstaller::ERROR) { + // Installation complete, move to the next step of the wizard + $oPage->add_ready_script( + <<', $sMessage); + $oPage->add_ready_script( + <<oWizard->SaveParameter('db_server', ''); + $this->oWizard->SaveParameter('db_user', ''); + $this->oWizard->SaveParameter('db_pwd', ''); + $this->oWizard->SaveParameter('db_name', ''); + $this->oWizard->SaveParameter('db_prefix', ''); + $this->oWizard->SaveParameter('new_db_name', ''); + $this->oWizard->SaveParameter('create_db', ''); + $this->oWizard->SaveParameter('db_new_name', ''); + $this->oWizard->SaveParameter('db_tls_enabled', false); + $this->oWizard->SaveParameter('db_tls_ca', ''); + + return ['class' => 'WizStepAdminAccount', 'state' => '']; + } + + public function Display(WebPage $oPage) + { + $oPage->add('

    Configuration of the database connection:

    '); + $sDBServer = $this->oWizard->GetParameter('db_server', ''); + $sDBUser = $this->oWizard->GetParameter('db_user', ''); + $sDBPwd = $this->oWizard->GetParameter('db_pwd', ''); + $sDBName = $this->oWizard->GetParameter('db_name', ''); + $sDBPrefix = $this->oWizard->GetParameter('db_prefix', ''); + $sTlsEnabled = $this->oWizard->GetParameter('db_tls_enabled', ''); + $sTlsCA = $this->oWizard->GetParameter('db_tls_ca', ''); + $sNewDBName = $this->oWizard->GetParameter('db_new_name', false); + + $oPage->add(''); + SetupUtils::DisplayDBParameters( + $oPage, + true, + $sDBServer, + $sDBUser, + $sDBPwd, + $sDBName, + $sDBPrefix, + $sTlsEnabled, + $sTlsCA, + $sNewDBName + ); + $sAuthentToken = $this->oWizard->GetParameter('authent', ''); + $oPage->add(''); + $oPage->add('
    '); + $sCreateDB = $this->oWizard->GetParameter('create_db', 'yes'); + if ($sCreateDB == 'no') { + $oPage->add_ready_script('$("#existing_db").prop("checked", true);'); + } else { + $oPage->add_ready_script('$("#create_db").prop("checked", true);'); + } + } + + public function AsyncAction(WebPage $oPage, $sCode, $aParameters) + { + switch ($sCode) { + case 'check_db': + SetupUtils::AsyncCheckDB($oPage, $aParameters); + break; + } + } + + /** + * Tells whether the "Next" button should be enabled interactively + * @return string A piece of javascript code returning either true or false + */ + public function JSCanMoveForward() + { + return + <<oWizard->SetParameter('mode', 'upgrade'); + $this->oWizard->SetParameter('upgrade_type', $sUpgradeType); + $bDisplayLicense = $this->oWizard->GetParameter('display_license'); + + switch ($sUpgradeType) { + case 'keep-previous': + $sSourceDir = utils::ReadParam('relative_source_dir', '', false, 'raw_data'); + $this->oWizard->SetParameter('source_dir', $this->oWizard->GetParameter('previous_version_dir').'/'.$sSourceDir); + $this->oWizard->SetParameter('datamodel_version', utils::ReadParam('datamodel_previous_version', '', false, 'raw_data')); + break; + + case 'use-compatible': + $sDataModelPath = utils::ReadParam('datamodel_path', '', false, 'raw_data'); + $this->oWizard->SetParameter('source_dir', $sDataModelPath); + $this->oWizard->SaveParameter('datamodel_version', ''); + break; + + default: + // Do nothing, maybe the user pressed the Back button + } + if ($bDisplayLicense) { + $aRet = ['class' => 'WizStepLicense2', 'state' => '']; + } else { + $aRet = ['class' => 'WizStepUpgradeMiscParams', 'state' => '']; + } + return $aRet; + } + + /** + * @param WebPage $oPage + * + * @throws Exception + */ + public function Display(WebPage $oPage) + { + $oPage->add_style( + <<bCanMoveForward = true; + $bDisplayLicense = true; + $sPreviousVersionDir = $this->oWizard->GetParameter('previous_version_dir', ''); + $aInstalledInfo = SetupUtils::GetApplicationVersion($this->oWizard); + + if ($aInstalledInfo === false) { + throw(new Exception('No previous version of '.ITOP_APPLICATION.' found in the supplied database. The upgrade cannot continue.')); + } elseif (strcasecmp($aInstalledInfo['product_name'], ITOP_APPLICATION) != 0) { + $oPage->p("Warning: The installed products seem different. Are you sure that you want to upgrade {$aInstalledInfo['product_name']} with ".ITOP_APPLICATION."?"); + } + + $sInstalledVersion = $aInstalledInfo['product_version']; + $sInstalledDataModelVersion = $aInstalledInfo['datamodel_version']; + + $oPage->add("

    Information about the upgrade from version $sInstalledVersion to ".ITOP_VERSION_FULL."

    "); + + if ($sInstalledVersion == ITOP_VERSION_FULL) { + // Reinstalling the same version let's skip the license agreement... + $bDisplayLicense = false; + } + $this->oWizard->SetParameter('license', $bDisplayLicense); // Remember for later + + $sCompatibleDMDir = SetupUtils::GetLatestDataModelDir(); + if ($sCompatibleDMDir === false) { + // No compatible version exists... cannot upgrade. Either it is too old, or too new (downgrade !) + $this->bCanMoveForward = false; + $oPage->p("No datamodel directory found."); + } else { + $sUpgradeDMVersion = SetupUtils::GetDataModelVersion($sCompatibleDMDir); + $sPreviousSourceDir = isset($aInstalledInfo['source_dir']) ? $aInstalledInfo['source_dir'] : 'modules'; + $aChanges = false; + if (is_dir($sPreviousVersionDir)) { + // Check if the previous version is a "genuine" one or not... + $aChanges = SetupUtils::CheckVersion($sInstalledDataModelVersion, $sPreviousVersionDir.'/'.$sPreviousSourceDir); + } + if (($aChanges !== false) && ((count($aChanges['added']) > 0) || (count($aChanges['removed']) > 0) || (count($aChanges['modified']) > 0))) { + // Some changes were detected, prompt the user to keep or discard them + $oPage->p(" Some modifications were detected between the ".ITOP_APPLICATION." version in '$sPreviousVersionDir' and a genuine $sInstalledVersion version."); + $oPage->p("What do you want to do?"); + + $aWritableDirs = ['modules', 'portal']; + $aErrors = SetupUtils::CheckWritableDirs($aWritableDirs); + $sChecked = ($this->oWizard->GetParameter('upgrade_type') == 'keep-previous') ? ' checked ' : ''; + $sDisabled = (count($aErrors) > 0) ? ' disabled ' : ''; + + $oPage->p(''); + $oPage->add(''); + + $oPage->add(''); + + if (count($aErrors) > 0) { + $oPage->p("Cannot copy the installed version due to the following access rights issue(s):"); + foreach ($aErrors as $sDir => $oCheckResult) { + $oPage->p(' '.$oCheckResult->sLabel); + } + } + + $sChecked = ($this->oWizard->GetParameter('upgrade_type') == 'use-compatible') ? ' checked ' : ''; + + $oPage->p(''); + + $oPage->add(''); + $oPage->add(''); + + $oPage->add('
    Details of the modifications
    '); + if (count($aChanges['added']) > 0) { + $oPage->add('
      New files added:'); + foreach ($aChanges['added'] as $sFilePath => $void) { + $oPage->add('
    • '.$sFilePath.'
    • '); + } + $oPage->add('
    '); + } + if (count($aChanges['removed']) > 0) { + $oPage->add('
      Deleted files:'); + foreach ($aChanges['removed'] as $sFilePath => $void) { + $oPage->add('
    • '.$sFilePath.'
    • '); + } + $oPage->add('
    '); + } + if (count($aChanges['modified']) > 0) { + $oPage->add('
      Modified files:'); + foreach ($aChanges['modified'] as $sFilePath => $void) { + $oPage->add('
    • '.$sFilePath.'
    • '); + } + $oPage->add('
    '); + } + $oPage->add('
    '); + } else { + // No changes detected... or no way to tell because of the lack of a manifest or previous source dir + // Use the "compatible" datamodel as-is. + $sCompatibleDMDirToDisplay = utils::HtmlEntities($sCompatibleDMDir); + $sUpgradeDMVersionToDisplay = utils::HtmlEntities($sUpgradeDMVersion); + $oPage->add( + <<The datamodel will be upgraded from version $sInstalledDataModelVersion to version $sUpgradeDMVersion. + + + +HTML + ); + + } + + $oPage->add_ready_script( + <<oWizard->GetParameter('db_name', '').$this->oWizard->GetParameter('db_prefix', ''), + $this->oWizard->GetParameter('db_server', ''), + $this->oWizard->GetParameter('db_user', ''), + $this->oWizard->GetParameter('db_pwd', ''), + $this->oWizard->GetParameter('db_tls_enabled', ''), + $this->oWizard->GetParameter('db_tls_ca', '') + ); + if ($oMutex->IsLocked()) { + $oPage->add('
    '.ITOP_APPLICATION.' cron process is being executed on the target database. '.ITOP_APPLICATION.' cron process will be stopped during the setup execution.
    '); + } + } + } + + public function CanMoveForward() + { + return $this->bCanMoveForward; + } + + /** + * Tells whether the "Next" button should be enabled interactively + * @return string A piece of javascript code returning either true or false + */ + public function JSCanMoveForward() + { + return + << 0); + return bRet; +EOF + ; + } +} diff --git a/setup/wizardsteps/WizStepDone.php b/setup/wizardsteps/WizStepDone.php new file mode 100644 index 000000000..35845c754 --- /dev/null +++ b/setup/wizardsteps/WizStepDone.php @@ -0,0 +1,168 @@ + '', 'state' => '']; + } + + public function Display(WebPage $oPage) + { + // Check if there are some manual steps required: + $aManualSteps = []; + $aAvailableModules = SetupUtils::AnalyzeInstallation($this->oWizard); + + $sRootUrl = utils::GetAbsoluteUrlAppRoot(true); + $aSelectedModules = json_decode($this->oWizard->GetParameter('selected_modules'), true); + foreach ($aSelectedModules as $sModuleId) { + if (!empty($aAvailableModules[$sModuleId]['doc.manual_setup'])) { + $sUrl = $aAvailableModules[$sModuleId]['doc.manual_setup']; + $sManualStepUrl = utils::IsURL($sUrl) ? $sUrl : $sRootUrl.$sUrl; + $aManualSteps[$aAvailableModules[$sModuleId]['label']] = $sManualStepUrl; + } + } + $oPage->add('
    '); + if (count($aManualSteps) > 0) { + $oPage->add("

    Manual operations required

    "); + $oPage->p("In order to complete the installation, the following manual operations are required:"); + foreach ($aManualSteps as $sModuleLabel => $sUrl) { + $oPage->p("Manual instructions for $sModuleLabel"); + } + $oPage->add("

    Congratulations for installing ".ITOP_APPLICATION."

    "); + } else { + $oPage->add("

    Congratulations for installing ".ITOP_APPLICATION."

    "); + $oPage->ok("The installation completed successfully."); + } + + $bHasBackup = false; + if (($this->oWizard->GetParameter('mode', '') == 'upgrade') && $this->oWizard->GetParameter('db_backup', false) && $this->oWizard->GetParameter('authent', false)) { + $sBackupDestination = $this->oWizard->GetParameter('db_backup_path', ''); + if (file_exists($sBackupDestination.'.tar.gz')) { + $bHasBackup = true; + // To mitigate security risks: pass only the filename without the extension, the download will add the extension itself + $oPage->p('Your backup is ready'); + $oPage->p(' Download '.basename($sBackupDestination).''); + } else { + $oPage->p(' Warning: Backup creation failed !'); + } + } + + // Form goes here.. No back button since the job is done ! + $oPage->add(''); + + $oPage->add('
    '); + + $oConfig = new Config(utils::GetConfigFilePath()); + $aParamValues = $this->oWizard->GetParamForConfigArray(); + $oConfig->UpdateFromParams($aParamValues); + // Load the data model only, in order to load env-production/core/main.php to get the XML parameters (needed by GetModuleSettings below) + // But main.php may also contain classes (defined without any module), and thus requiring the full data model + // to be loaded to prevent "class not found" errors... + $oProductionEnv = new RunTimeEnvironment('production'); + $oProductionEnv->InitDataModel($oConfig, true); + $sIframeUrl = $oConfig->GetModuleSetting('itop-hub-connector', 'setup_url', ''); + + $sSetupTokenFile = APPROOT.'data/.setup'; + $sSetupToken = bin2hex(random_bytes(12)); + file_put_contents($sSetupTokenFile, $sSetupToken); + $sIframeUrl .= "&setup_token=$sSetupToken"; + + if ($sIframeUrl != '') { + $oPage->add(''); + + $oPage->add_script(" + window.addEventListener('message', function(event) { + if (event.data === 'itophub_load_completed') + { + $('#placeholder').hide(); + $('#fresh_content').show(); + } + }, false); + "); + } + + $sForm = '
    '; + $sForm .= ''; + $sForm .= ''; + $sForm .= "
    "; + $sForm .= ''; + + $sForm = addslashes($sForm); + $oPage->add_ready_script("$('#wiz_form').append('$sForm');"); + // avoid leaving in a dirty state + SetupUtils::ExitMaintenanceMode(false); + SetupUtils::ExitReadOnlyMode(false); + + if (false === $bHasBackup) { + SetupUtils::EraseSetupToken(); + } + } + + public function CanMoveForward() + { + return false; + } + public function CanMoveBackward() + { + return false; + } + + /** + * 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 false; //This step executes once the config was written and secured + } + + public function AsyncAction(WebPage $oPage, $sCode, $aParameters) + { + SetupUtils::EraseSetupToken(); + // 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)) { + // Make sure there is NO output at all before our content, otherwise the document will be corrupted + $sPreviousContent = ob_get_clean(); + $oPage->SetContentType('application/gzip'); + $oPage->SetContentDisposition('attachment', basename($sBackupFile)); + $oPage->add(file_get_contents($sBackupFile)); + } + } +} diff --git a/setup/wizardsteps/WizStepInstallMiscParams.php b/setup/wizardsteps/WizStepInstallMiscParams.php new file mode 100644 index 000000000..92e392696 --- /dev/null +++ b/setup/wizardsteps/WizStepInstallMiscParams.php @@ -0,0 +1,183 @@ +oWizard->SaveParameter('default_language', ''); + $this->oWizard->SaveParameter('application_url', ''); + $this->oWizard->SaveParameter('graphviz_path', ''); + $this->oWizard->SaveParameter('sample_data', 'yes'); + return ['class' => 'WizStepModulesChoice', 'state' => 'start_install']; + } + + public function Display(WebPage $oPage) + { + $sDefaultLanguage = $this->oWizard->GetParameter('default_language', $this->oWizard->GetParameter('admin_language')); + $sApplicationURL = $this->oWizard->GetParameter('application_url', utils::GetDefaultUrlAppRoot(true)); + $sDefaultGraphvizPath = (strtolower(substr(PHP_OS, 0, 3)) === 'win') ? 'C:\\Program Files\\Graphviz\\bin\\dot.exe' : '/usr/bin/dot'; + $sGraphvizPath = $this->oWizard->GetParameter('graphviz_path', $sDefaultGraphvizPath); + $sSampleData = $this->oWizard->GetParameter('sample_data', 'yes'); + $oPage->add('

    Additional parameters

    '); + $oPage->add('
    '); + $oPage->add('Default Language'); + $oPage->add(''); + $sSourceDir = APPROOT.'dictionaries/'; + $aLanguages = SetupUtils::GetAvailableLanguages($sSourceDir); + $oPage->add(''); + $oPage->add('
    Default Language: '); + $oPage->add(SetupUtils::GetLanguageSelect($sSourceDir, 'default_language', $sDefaultLanguage)); + $oPage->add('
    '); + $oPage->add('
    '); + $oPage->add('
    '); + $oPage->add('Application URL'); + $oPage->add(''); + $oPage->add(''); + $oPage->add('
    URL:
    '); + $oPage->add('
    Change the value above if the end-users will be accessing the application by another path due to a specific configuration of the web server.
    '); + $oPage->add('
    '); + $oPage->add('
    '); + $oPage->add('Path to Graphviz\' dot application'); + $oPage->add(''); + $oPage->add(''); + $oPage->add(''); + $oPage->add('
    Path:
    '); + $oPage->add(''); + $oPage->add('
    '); + $oPage->add('
    '); + $oPage->add('Sample Data'); + $sChecked = ($sSampleData == 'yes') ? 'checked ' : ''; + $oPage->p('
    '); + $sAuthentToken = $this->oWizard->GetParameter('authent', ''); + $oPage->add(''); + $oPage->add_ready_script( + <<AddUseSymlinksFlagOption($oPage); + } + + public function AsyncAction(WebPage $oPage, $sCode, $aParameters) + { + switch ($sCode) { + case 'check_graphviz': + $sGraphvizPath = $aParameters['graphviz_path']; + $aCheck = SetupUtils::CheckGraphviz($sGraphvizPath); + + // N°2214 logging TRACE results + $aTraceCheck = CheckResult::FilterCheckResultArray($aCheck, [CheckResult::TRACE]); + foreach ($aTraceCheck as $oTraceCheck) { + SetupLog::Ok($oTraceCheck->sLabel); + } + + $aNonTraceCheck = array_diff($aCheck, $aTraceCheck); + foreach ($aNonTraceCheck as $oCheck) { + switch ($oCheck->iSeverity) { + case CheckResult::INFO: + $sStatus = 'ok'; + $sInfoExplanation = $oCheck->sLabel; + $sMessage = json_encode('
    '.$sInfoExplanation.'
    '); + + break; + + default: + case CheckResult::ERROR: + case CheckResult::WARNING: + $sStatus = 'ko'; + $sErrorExplanation = $oCheck->sLabel; + $sMessage = json_encode('
    '.$sErrorExplanation.'
    '); + break; + } + + if ($oCheck->iSeverity !== CheckResult::TRACE) { + $oPage->add_ready_script( + <<'); + } + else + { + $("#v_application_url").html(''); + } + bGraphviz = ($('#graphviz_path').val() != ''); + if (!bGraphviz) + { + // Does not prevent to move forward + $("#v_graphviz_path").html(''); + } + else + { + $("#v_graphviz_path").html(''); + } + return bRet; +EOF + ; + } +} diff --git a/setup/wizardsteps/WizStepInstallOrUpgrade.php b/setup/wizardsteps/WizStepInstallOrUpgrade.php new file mode 100644 index 000000000..a3d4b3ddd --- /dev/null +++ b/setup/wizardsteps/WizStepInstallOrUpgrade.php @@ -0,0 +1,254 @@ +oWizard->SaveParameter('previous_version_dir', ''); + $this->oWizard->SaveParameter('db_server', ''); + $this->oWizard->SaveParameter('db_user', ''); + $this->oWizard->SaveParameter('db_pwd', ''); + $this->oWizard->SaveParameter('db_name', ''); + $this->oWizard->SaveParameter('db_prefix', ''); + $this->oWizard->SaveParameter('db_backup', false); + $this->oWizard->SaveParameter('db_backup_path', ''); + $this->oWizard->SaveParameter('db_tls_enabled', false); + $this->oWizard->SaveParameter('db_tls_ca', ''); + + if ($sInstallMode == 'install') { + $this->oWizard->SetParameter('install_mode', 'install'); + $sFullSourceDir = SetupUtils::GetLatestDataModelDir(); + $this->oWizard->SetParameter('source_dir', $sFullSourceDir); + $this->oWizard->SetParameter('datamodel_version', SetupUtils::GetDataModelVersion($sFullSourceDir)); + $sNextStep = 'WizStepLicense'; + } else { + $this->oWizard->SetParameter('install_mode', 'upgrade'); + $sNextStep = 'WizStepDetectedInfo'; + + } + return ['class' => $sNextStep, 'state' => '']; + } + + public function Display(WebPage $oPage) + { + $sInstallMode = $this->oWizard->GetParameter('install_mode', ''); + $sDBServer = $this->oWizard->GetParameter('db_server', ''); + $sDBUser = $this->oWizard->GetParameter('db_user', ''); + $sDBPwd = $this->oWizard->GetParameter('db_pwd', ''); + $sDBName = $this->oWizard->GetParameter('db_name', ''); + $sDBPrefix = $this->oWizard->GetParameter('db_prefix', ''); + $bDBBackup = $this->oWizard->GetParameter('db_backup', false); + $sDBBackupPath = $this->oWizard->GetParameter('db_backup_path', ''); + $sTlsEnabled = $this->oWizard->GetParameter('db_tls_enabled', false); + $sTlsCA = $this->oWizard->GetParameter('db_tls_ca', ''); + $sMySQLBinDir = $this->oWizard->GetParameter('mysql_bindir', null); + $sPreviousVersionDir = ''; + if ($sInstallMode == '') { + $sDBBackupPath = utils::GetDataPath().'backups/manual/setup-'.date('Y-m-d_H_i'); + $bDBBackup = true; + $aPreviousInstance = SetupUtils::GetPreviousInstance(APPROOT); + if ($aPreviousInstance['found']) { + $sInstallMode = 'upgrade'; + $sDBServer = $aPreviousInstance['db_server']; + $sDBUser = $aPreviousInstance['db_user']; + $sDBPwd = $aPreviousInstance['db_pwd']; + $sDBName = $aPreviousInstance['db_name']; + $sDBPrefix = $aPreviousInstance['db_prefix']; + $sTlsEnabled = $aPreviousInstance['db_tls_enabled']; + $sTlsCA = $aPreviousInstance['db_tls_ca']; + $this->oWizard->SaveParameter('graphviz_path', $aPreviousInstance['graphviz_path']); + $sMySQLBinDir = $aPreviousInstance['mysql_bindir']; + $this->oWizard->SaveParameter('mysql_bindir', $aPreviousInstance['mysql_bindir']); + $sPreviousVersionDir = APPROOT; + } else { + $sInstallMode = 'install'; + } + } + $sPreviousVersionDir = $this->oWizard->GetParameter('previous_version_dir', $sPreviousVersionDir); + + $sUpgradeInfoStyle = ''; + if ($sInstallMode == 'install') { + $sUpgradeInfoStyle = ' style="display: none;" '; + } + $oPage->add('
    What do you want to do?
    '); + $sChecked = ($sInstallMode == 'install') ? ' checked ' : ''; + $oPage->p(''); + $sChecked = ($sInstallMode == 'upgrade') ? ' checked ' : ''; + $sDisabled = (($sInstallMode == 'install') && (empty($sPreviousVersionDir))) ? ' disabled' : ''; + $oPage->p(''); + + $sUpgradeDir = utils::HtmlEntities($sPreviousVersionDir); + $oPage->add( + << +
    Location on the disk: +
    +HTML + ); + + SetupUtils::DisplayDBParameters( + $oPage, + false, + $sDBServer, + $sDBUser, + $sDBPwd, + $sDBName, + $sDBPrefix, + $sTlsEnabled, + $sTlsCA, + null + ); + + $aBackupChecks = SetupUtils::CheckBackupPrerequisites($sDBBackupPath, $sMySQLBinDir); + $bCanBackup = true; + $sMySQLDumpMessage = ''; + foreach ($aBackupChecks as $oCheck) { + switch ($oCheck->iSeverity) { + case CheckResult::ERROR: + $bCanBackup = false; + $sMySQLDumpMessage .= '
    Error:'.$oCheck->sLabel.'
    '; + break; + case CheckResult::TRACE: + SetupLog::Ok($oCheck->sLabel); + break; + default: + $sMySQLDumpMessage .= '
    Success:'.$oCheck->sLabel.'
    '; + break; + } + } + $sChecked = ($bCanBackup && $bDBBackup) ? ' checked ' : ''; + $sDisabled = $bCanBackup ? '' : ' disabled '; + $oPage->add(''); + $oPage->add('
    Save the backup to:
    '); + $fFreeSpace = SetupUtils::CheckDiskSpace($sDBBackupPath); + $sMessage = ''; + if ($fFreeSpace !== false) { + $sMessage .= SetupUtils::HumanReadableSize($fFreeSpace).' free in '.dirname($sDBBackupPath); + } + $oPage->add($sMySQLDumpMessage.''.$sMessage.''); + $oPage->add(''); + $sAuthentToken = $this->oWizard->GetParameter('authent', ''); + $oPage->add(''); + //$oPage->add(''); + $oPage->add_ready_script( + <<add_ready_script( + <<add_ready_script( + <<add_ready_script( + <<oWizard->SaveParameter('accept_license', 'no'); + return ['class' => 'WizStepDBParams', 'state' => '']; + } + + /** + * @return bool true if we need to display a GDPR confirmation + * @throws \Exception + * @since 2.7.7 3.0.2 3.1.0 N°5037 method creation + * @since 2.7.8 3.0.3 3.1.0 N°5758 rename from NeedsRgpdConsent to NeedsGdprConsent + */ + private function NeedsGdprConsent() + { + $sMode = $this->oWizard->GetParameter('install_mode'); + + if ($sMode !== 'install') { + return false; + } + + $aModules = SetupUtils::AnalyzeInstallation($this->oWizard); + return SetupUtils::IsConnectableToITopHub($aModules); + } + + /** + * @param WebPage $oPage + */ + public function Display(WebPage $oPage) + { + $aLicenses = SetupUtils::GetLicenses(); + $oPage->add_style( + <<add('

    Licenses agreements for the components of '.ITOP_APPLICATION.'

    '); + $oPage->add_style('div a.no-arrow { background:transparent; padding-left:0;}'); + $oPage->add_style('.toggle { cursor:pointer; text-decoration:underline; color:#1C94C4; }'); + $oPage->add('
    '); + $oPage->add('Components of '.ITOP_APPLICATION.''); + $oPage->add('
      '); + $index = 0; + foreach ($aLicenses as $oLicense) { + $oPage->add('
    • '.$oLicense->product.', © '.$oLicense->author.' is licensed under the '.$oLicense->license_type.' license. (Details)'); + $oPage->add(''); + $oPage->add_ready_script('$(".license_text a").attr("target", "_blank").addClass("no-arrow");'); + $oPage->add_ready_script('$("#toggle_'.$index.'").on("click", function() { $("#license_'.$index.'").toggle(); } );'); + $index++; + } + $oPage->add('
    '); + $oPage->add('
    '); + $sChecked = ($this->oWizard->GetParameter('accept_license', 'no') == 'yes') ? ' checked ' : ''; + $oPage->add('
    '); + if ($this->NeedsGdprConsent()) { + $oPage->add('
    '); + $oPage->add('
    '); + $oPage->add('European General Data Protection Regulation'); + $oPage->add('
    '.ITOP_APPLICATION.' software is compliant with the processing of personal data according to the European General Data Protection Regulation (GDPR).

    +By installing '.ITOP_APPLICATION.' you agree that some information will be collected by Combodo to help you manage your instances and for statistical purposes. +This data remains anonymous until it is associated to a user account on iTop Hub.

    +

    List of collected data available in our Data privacy section.


    '); + $oPage->add(''); + $oPage->add(''); + $oPage->add('
    '); + } + $oPage->add_ready_script('$(".check_select").on("click change", function() { WizardUpdateButtons(); });'); + + $oPage->add_script( + << 'WizStepUpgradeMiscParams', 'state' => '']; + } +} diff --git a/setup/wizardsteps/WizStepModulesChoice.php b/setup/wizardsteps/WizStepModulesChoice.php new file mode 100644 index 000000000..57268a130 --- /dev/null +++ b/setup/wizardsteps/WizStepModulesChoice.php @@ -0,0 +1,820 @@ +bChoicesFromDatabase = false; + $this->oExtensionsMap = new iTopExtensionsMap(); + $sPreviousSourceDir = $this->oWizard->GetParameter('previous_version_dir', ''); + $sConfigPath = null; + if (($sPreviousSourceDir !== '') && is_readable($sPreviousSourceDir.'/conf/production/config-itop.php')) { + $sConfigPath = $sPreviousSourceDir.'/conf/production/config-itop.php'; + } elseif (is_readable(utils::GetConfigFilePath('production'))) { + $sConfigPath = utils::GetConfigFilePath('production'); + } + + // only called if the config file exists : we are updating a previous installation ! + // WARNING : we can't load this config directly, as it might be from another directory with a different approot_url (N°2684) + if ($sConfigPath !== null) { + $this->oConfig = new Config($sConfigPath); + + $aParamValues = $oWizard->GetParamForConfigArray(); + $this->oConfig->UpdateFromParams($aParamValues); + + $this->oExtensionsMap->LoadChoicesFromDatabase($this->oConfig); + $this->bChoicesFromDatabase = true; + } + + // Sanity check (not stopper, to let developers go further...) + try { + $this->aAnalyzeInstallationModules = SetupUtils::AnalyzeInstallation($this->oWizard, true); + } catch (MissingDependencyException $e) { + $this->oMissingDependencyException = $e; + $this->aAnalyzeInstallationModules = SetupUtils::AnalyzeInstallation($this->oWizard); + } + } + + public function GetTitle(): string + { + $aStepInfo = $this->GetStepInfo(); + + return $aStepInfo['title'] ?? 'Modules selection'; + } + + public function GetPossibleSteps() + { + return ['WizStepModulesChoice', 'WizStepAudit']; + } + + public function GetAddedAndRemovedExtensions($aSelectedExtensions) + { + $aExtensionsAdded = []; + $aExtensionsRemoved = []; + $aExtensionsNotUninstallable = []; + foreach ($this->oExtensionsMap->GetAllExtensionsWithPreviouslyInstalled() as $oExtension) { + /* @var \iTopExtension $oExtension */ + $bSelected = in_array($oExtension->sCode, $aSelectedExtensions); + if ($oExtension->bInstalled && !$bSelected) { + $aExtensionsRemoved[$oExtension->sCode] = $oExtension->sLabel; + if (!$oExtension->CanBeUninstalled()) { + $aExtensionsNotUninstallable[$oExtension->sCode] = true; + } + } elseif (!$oExtension->bInstalled && $bSelected) { + $aExtensionsAdded[$oExtension->sCode] = $oExtension->sLabel; + } + } + + return [$aExtensionsAdded, $aExtensionsRemoved, $aExtensionsNotUninstallable]; + } + + public function ProcessParams($bMoveForward = true) + { + // Accumulates the selected modules: + $index = $this->GetStepIndex(); + + // use json_encode:decode to store a hash array: step_id => array(input_name => selected_input_id) + $aSelectedChoices = json_decode($this->oWizard->GetParameter('selected_components', '{}'), true); + $aSelected = utils::ReadParam('choice', []); + $aSelectedChoices[$index] = $aSelected; + $this->oWizard->SetParameter('selected_components', json_encode($aSelectedChoices)); + + if ($this->GetStepInfo($index) == null) { + throw new Exception('Internal error: invalid step "'.$index.'" for the choice of modules.'); + } elseif ($bMoveForward) { + if ($this->GetStepInfo(1 + $index) != null) { + return ['class' => 'WizStepModulesChoice', 'state' => (1 + $index)]; + } else { + // Exiting this step of the wizard, let's convert the selection into a list of modules + $aModules = []; + $aExtensions = []; + $sDisplayChoices = '
      '; + for ($i = 0; $i <= $index; $i++) { + $aStepInfo = $this->GetStepInfo($i); + $sDisplayChoices .= $this->GetSelectedModules($aStepInfo, $aSelectedChoices[$i], $aModules, '', '', $aExtensions); + } + $sDisplayChoices .= '
    '; + if (class_exists('CreateITILProfilesInstaller')) { + $this->oWizard->SetParameter('old_addon', true); + } + + [$aExtensionsAdded, $aExtensionsRemoved, $aExtensionsNotUninstallable] = $this->GetAddedAndRemovedExtensions($aExtensions); + $this->oWizard->SetParameter('selected_modules', json_encode(array_keys($aModules))); + $this->oWizard->SetParameter('selected_extensions', json_encode($aExtensions)); + $this->oWizard->SetParameter('display_choices', $sDisplayChoices); + $this->oWizard->SetParameter('extensions_added', json_encode($aExtensionsAdded)); + $this->oWizard->SetParameter('removed_extensions', json_encode($aExtensionsRemoved)); + $this->oWizard->SetParameter('extensions_not_uninstallable', json_encode(array_keys($aExtensionsNotUninstallable))); + $sMode = $this->oWizard->GetParameter('mode', 'install'); + if ($sMode == 'install') { + return ['class' => 'WizStepAudit', 'state' => '']; + } + else { + return ['class' => 'WizStepAudit', 'state' => '']; + } + + } + + } + } + + public function Display(WebPage $oPage) + { + $this->DisplayStep($oPage); + } + + /** + * @param \SetupPage $oPage + * + * @throws \Exception + */ + protected function DisplayStep($oPage) + { + // Sanity check (not stopper, to let developers go further...) + if (! is_null($this->oMissingDependencyException)) { + $oPage->warning($this->oMissingDependencyException->getHtmlDesc(), $this->oMissingDependencyException->getMessage()); + } + + $this->bUpgrade = ($this->oWizard->GetParameter('install_mode') != 'install'); + $aStepInfo = $this->GetStepInfo(); + $oPage->add_style("div.choice { margin: 0.5em;}"); + $oPage->add_style("div.choice a { text-decoration:none; font-weight: bold; color: #1C94C4 }"); + $oPage->add_style("div.description { margin-left: 2em; }"); + $oPage->add_style(".choice-disabled { color: #999; }"); + $oPage->add_style("input.unremovable { accent-color: orangered;}"); + + $sManualInstallError = SetupUtils::CheckManualInstallDirEmpty( + $this->aAnalyzeInstallationModules, + $this->oWizard->GetParameter('extensions_dir', 'extensions') + ); + if ($sManualInstallError !== '') { + $oPage->warning($sManualInstallError); + } + + $oPage->add('
    '); + $sBannerPath = isset($aStepInfo['banner']) ? $aStepInfo['banner'] : ''; + if (!empty($sBannerPath)) { + if (substr($sBannerPath, 0, 1) == '/') { + // absolute path, means relative to APPROOT + $sBannerUrl = utils::GetDefaultUrlAppRoot(true).$sBannerPath; + } else { + // relative path: i.e. relative to the directory containing the XML file + $sFullPath = dirname($this->GetSourceFilePath()).'/'.$sBannerPath; + $sRealPath = realpath($sFullPath); + $sBannerUrl = utils::GetDefaultUrlAppRoot(true).str_replace(realpath(APPROOT), '', $sRealPath); + } + $oPage->add(''); + } + $sDescription = $aStepInfo['description'] ?? ''; + $oPage->add(''.$sDescription.''); + $oPage->add('
    '); + + // Build the default choices + $aDefaults = $this->GetDefaults($aStepInfo, $this->aAnalyzeInstallationModules); + $index = $this->GetStepIndex(); + + // retrieve the saved selection + // use json_encode:decode to store a hash array: step_id => array(input_name => selected_input_id) + $aParameters = json_decode($this->oWizard->GetParameter('selected_components', '{}'), true); + if (!isset($aParameters[$index])) { + $aParameters[$index] = $aDefaults; + } + $aSelectedComponents = $aParameters[$index]; + + $oPage->add('
    '); + $this->DisplayOptions($oPage, $aStepInfo, $aSelectedComponents, $aDefaults); + $oPage->add('
    '); + + $oPage->add_script( + <<add_ready_script( + <<bChoicesFromDatabase) { + $this->GuessDefaultsFromModules($aInfo, $aDefaults, $aModules, $sParentId); + } else { + $this->GetDefaultsFromDatabase($aInfo, $aDefaults, $sParentId); + } + return $aDefaults; + } + + protected function GetDefaultsFromDatabase($aInfo, &$aDefaults, $sParentId) + { + $aOptions = isset($aInfo['options']) ? $aInfo['options'] : []; + foreach ($aOptions as $index => $aChoice) { + $sChoiceId = $sParentId.self::$SEP.$index; + if ($this->bUpgrade) { + if ($this->oExtensionsMap->IsMarkedAsChosen($aChoice['extension_code'])) { + $aDefaults[$sChoiceId] = $sChoiceId; + } + } elseif (isset($aChoice['default']) && $aChoice['default']) { + $aDefaults[$sChoiceId] = $sChoiceId; + } + // Recurse for sub_options (if any) + if (isset($aChoice['sub_options'])) { + $this->GetDefaultsFromDatabase($aChoice['sub_options'], $aDefaults, $sChoiceId); + } + } + + $aAlternatives = $aInfo['alternatives'] ?? []; + $sChoiceName = null; + foreach ($aAlternatives as $index => $aChoice) { + $sChoiceId = $sParentId.self::$SEP.$index; + if ($sChoiceName == null) { + $sChoiceName = $sChoiceId; // All radios share the same name + } + if ($this->bUpgrade) { + if ($this->oExtensionsMap->IsMarkedAsChosen($aChoice['extension_code'])) { + $aDefaults[$sChoiceName] = $sChoiceId; + } + } elseif (isset($aChoice['default']) && $aChoice['default']) { + $aDefaults[$sChoiceName] = $sChoiceId; + } + // Recurse for sub_options (if any) + if (isset($aChoice['sub_options'])) { + $this->GetDefaultsFromDatabase($aChoice['sub_options'], $aDefaults, $sChoiceId); + } + } + } + + /** + * Try to guess the user choices based on the current list of installed modules... + * @param array $aInfo + * @param array $aDefaults + * @param array $aModules + * @param string $sParentId + * @return array + */ + protected function GuessDefaultsFromModules($aInfo, &$aDefaults, $aModules, $sParentId = '') + { + $aRetScore = []; + $aScores = []; + + $aOptions = isset($aInfo['options']) ? $aInfo['options'] : []; + foreach ($aOptions as $index => $aChoice) { + $sChoiceId = $sParentId.self::$SEP.$index; + $aScores[$sChoiceId] = []; + if (!$this->bUpgrade && isset($aChoice['default']) && $aChoice['default']) { + $aDefaults[$sChoiceId] = $sChoiceId; + } + if ($this->bUpgrade) { + // In upgrade mode, the defaults are the installed modules + foreach ($aChoice['modules'] as $sModuleId) { + if ($aModules[$sModuleId]['installed_version'] != '') { + // A module corresponding to this choice is installed + $aScores[$sChoiceId][$sModuleId] = true; + } + } + // Used for migration from 1.3.x or before + // Accept that the new version can have one new module than the previous version + // The option is still selected + $iSelected = count($aScores[$sChoiceId]); + $iNeeded = count($aChoice['modules']); + if (($iSelected > 0) && (($iNeeded - $iSelected) < 2)) { + // All the modules are installed, this choice is selected + $aDefaults[$sChoiceId] = $sChoiceId; + } + $aRetScore = array_merge($aRetScore, $aScores[$sChoiceId]); + } + + if (isset($aChoice['sub_options'])) { + $aScores[$sChoiceId] = array_merge($aScores[$sChoiceId], $this->GuessDefaultsFromModules($aChoice['sub_options'], $aDefaults, $sChoiceId)); + } + $index++; + } + + $aAlternatives = isset($aInfo['alternatives']) ? $aInfo['alternatives'] : []; + $sChoiceName = null; + $sChoiceIdNone = null; + foreach ($aAlternatives as $index => $aChoice) { + $sChoiceId = $sParentId.self::$SEP.$index; + $aScores[$sChoiceId] = []; + if ($sChoiceName == null) { + $sChoiceName = $sChoiceId; + } + if (!$this->bUpgrade && isset($aChoice['default']) && $aChoice['default']) { + $aDefaults[$sChoiceName] = $sChoiceId; + } + if (isset($aChoice['sub_options'])) { + // By default (i.e. install-mode), sub options can only be checked if the parent option is checked by default + if ($this->bUpgrade || (isset($aChoice['default']) && $aChoice['default'])) { + $aScores[$sChoiceId] = $this->GuessDefaultsFromModules($aChoice['sub_options'], $aDefaults, $aModules, $sChoiceId); + } + } + $index++; + } + + $iMaxScore = 0; + if ($this->bUpgrade && (count($aAlternatives) > 0)) { + // The installed choices have precedence over the 'default' choices + // In case several choices share the same base modules, let's weight the alternative choices + // based on their number of installed modules + $sChoiceName = null; + + foreach ($aAlternatives as $index => $aChoice) { + $sChoiceId = $sParentId.self::$SEP.$index; + if ($sChoiceName == null) { + $sChoiceName = $sChoiceId; + } + if (array_key_exists('modules', $aChoice)) { + foreach ($aChoice['modules'] as $sModuleId) { + if ($aModules[$sModuleId]['installed_version'] != '') { + // A module corresponding to this choice is installed, increase the score of this choice + if (!isset($aScores[$sChoiceId])) { + $aScores[$sChoiceId] = []; + } + $aScores[$sChoiceId][$sModuleId] = true; + $iMaxScore = max($iMaxScore, count($aScores[$sChoiceId])); + } + } + //if (count($aScores[$sChoiceId]) == count($aChoice['modules'])) + //{ + // $iScore += 100; // Bonus for the parent when a choice is complete + //} + $aRetScore = array_merge($aRetScore, $aScores[$sChoiceId]); + } + $iMaxScore = max($iMaxScore, isset($aScores[$sChoiceId]) ? count($aScores[$sChoiceId]) : 0); + } + } + if ($iMaxScore > 0) { + $aNumericScores = []; + foreach ($aScores as $sChoiceId => $aModules) { + $aNumericScores[$sChoiceId] = count($aModules); + } + // The choice with the bigger score wins ! + asort($aNumericScores, SORT_NUMERIC); + $aKeys = array_keys($aNumericScores); + $sBetterChoiceId = array_pop($aKeys); + $aDefaults[$sChoiceName] = $sBetterChoiceId; + } + // echo "Scores:
    ".print_r($aScores, true)."

    "; + // echo "Defaults:
    ".print_r($aDefaults, true)."

    "; + return $aRetScore; + } + + private function GetPhpExpressionEvaluator(): PhpExpressionEvaluator + { + if (!isset($this->oPhpExpressionEvaluator)) { + $this->oPhpExpressionEvaluator = new PhpExpressionEvaluator([], RunTimeEnvironment::STATIC_CALL_AUTOSELECT_WHITELIST); + } + + return $this->oPhpExpressionEvaluator; + } + + /** + * Converts the list of selected "choices" into a list of "modules": take into account the selected and the mandatory modules + * + * @param array $aInfo Info about the "choice" array('options' => array(...), 'alternatives' => array(...)) + * @param array $aSelectedChoices List of selected choices array('name' => 'selected_value_id') + * @param array $aModules Return parameter: List of selected modules array('module_id' => true) + * @param string $sParentId Used for recursion + * + * @return string A text representation of what will be installed + */ + protected function GetSelectedModules($aInfo, $aSelectedChoices, &$aModules, $sParentId = '', $sDisplayChoices = '', &$aSelectedExtensions = null) + { + if ($sParentId == '') { + // Check once (before recursing) that the hidden modules are selected + foreach ($this->aAnalyzeInstallationModules as $sModuleId => $aModule) { + if (($sModuleId != ROOT_MODULE) && !isset($aModules[$sModuleId])) { + if (($aModule['category'] == 'authentication') || (!$aModule['visible'] && !isset($aModule['auto_select']))) { + $aModules[$sModuleId] = true; + $sDisplayChoices .= '
  • '.$aModule['label'].' (hidden)
  • '; + } + } + } + } + $aOptions = isset($aInfo['options']) ? $aInfo['options'] : []; + foreach ($aOptions as $index => $aChoice) { + $sChoiceId = $sParentId.self::$SEP.$index; + $aModuleInfo = []; + // Get the extension corresponding to the choice + foreach ($this->oExtensionsMap->GetAllExtensions() as $sExtensionVersion => $oExtension) { + if (utils::StartsWith($sExtensionVersion, $aChoice['extension_code'].'/')) { + $aModuleInfo = $oExtension->aModuleInfo; + break; + } + } + if ((isset($aChoice['mandatory']) && $aChoice['mandatory']) || + (isset($aSelectedChoices[$sChoiceId]) && ($aSelectedChoices[$sChoiceId] == $sChoiceId))) { + $sDisplayChoices .= '
  • '.$aChoice['title'].'
  • '; + if (isset($aChoice['modules'])) { + foreach ($aChoice['modules'] as $sModuleId) { + $bSelected = true; + if (isset($aModuleInfo[$sModuleId])) { + // Test if module has 'auto_select' + $aInfo = $aModuleInfo[$sModuleId]; + if (isset($aInfo['auto_select'])) { + // Check the module selection + try { + SetupInfo::SetSelectedModules($aModules); + $bSelected = $this->GetPhpExpressionEvaluator()->ParseAndEvaluateBooleanExpression($aInfo['auto_select']); + } catch (ModuleFileReaderException $e) { + //logged already + $bSelected = false; + } + } + } + if ($bSelected) { + $aModules[$sModuleId] = true; // store the Id of the selected module + SetupInfo::SetSelectedModules($aModules); + } + } + } + $sChoiceType = isset($aChoice['type']) ? $aChoice['type'] : 'wizard_option'; + if ($aSelectedExtensions !== null) { + $aSelectedExtensions[] = $aChoice['extension_code']; + } + // Recurse only for selected choices + if (isset($aChoice['sub_options'])) { + $sDisplayChoices .= '
      '; + $sDisplayChoices = $this->GetSelectedModules($aChoice['sub_options'], $aSelectedChoices, $aModules, $sChoiceId, $sDisplayChoices, $aSelectedExtensions); + $sDisplayChoices .= '
    '; + } + $sDisplayChoices .= ''; + } + } + + $aAlternatives = isset($aInfo['alternatives']) ? $aInfo['alternatives'] : []; + $sChoiceName = null; + foreach ($aAlternatives as $index => $aChoice) { + $sChoiceId = $sParentId.self::$SEP.$index; + if ($sChoiceName == null) { + $sChoiceName = $sChoiceId; + } + if ((isset($aChoice['mandatory']) && $aChoice['mandatory']) || + (isset($aSelectedChoices[$sChoiceName]) && ($aSelectedChoices[$sChoiceName] == $sChoiceId))) { + $sDisplayChoices .= '
  • '.$aChoice['title'].'
  • '; + if ($aSelectedExtensions !== null) { + $aSelectedExtensions[] = $aChoice['extension_code']; + } + if (isset($aChoice['modules'])) { + foreach ($aChoice['modules'] as $sModuleId) { + $aModules[$sModuleId] = true; // store the Id of the selected module + } + } + // Recurse only for selected choices + if (isset($aChoice['sub_options'])) { + $sDisplayChoices .= '
      '; + $sDisplayChoices = $this->GetSelectedModules($aChoice['sub_options'], $aSelectedChoices, $aModules, $sChoiceId, $sDisplayChoices, $aSelectedExtensions); + $sDisplayChoices .= '
    '; + } + $sDisplayChoices .= ''; + } + } + if ($sParentId == '') { + // Last pass (after all the user's choices are turned into "selected" modules): + // Process 'auto_select' modules for modules that are not already selected + do { + // Loop while new modules are added... + $bModuleAdded = false; + foreach ($this->aAnalyzeInstallationModules as $sModuleId => $aModule) { + if (($sModuleId != ROOT_MODULE) && !array_key_exists($sModuleId, $aModules) && isset($aModule['auto_select'])) { + try { + SetupInfo::SetSelectedModules($aModules); + $bSelected = $this->GetPhpExpressionEvaluator()->ParseAndEvaluateBooleanExpression($aModule['auto_select']); + if ($bSelected) { + $aModules[$sModuleId] = true; // store the Id of the selected module + $sDisplayChoices .= '
  • '.$aModule['label'].' (auto_select)
  • '; + $bModuleAdded = true; + } + } catch (ModuleFileReaderException $e) { + //logged already + $sDisplayChoices .= '
  • Warning: auto_select failed with exception ('.$e->getMessage().') for module "'.$sModuleId.'"
  • '; + } + } + } + } while ($bModuleAdded); + } + + return $sDisplayChoices; + } + + protected function GetStepIndex() + { + switch ($this->sCurrentState) { + case 'start_install': + case 'start_upgrade': + $index = 0; + break; + + default: + $index = (int)$this->sCurrentState; + } + return $index; + } + + protected function GetStepInfo($idx = null) + { + $index = $idx ?? $this->GetStepIndex(); + + if (is_null($this->aSteps)) { + $this->oWizard->SetParameter('additional_extensions_modules', json_encode([])); // Default value, no additional extensions + + $aOptions = $this->oExtensionsMap->GetAllExtensionsOptionInfo(); + if (@file_exists($this->GetSourceFilePath())) { + // Found an "installation.xml" file, let's use this definition for the wizard + $aParams = new XMLParameters($this->GetSourceFilePath()); + $this->aSteps = $aParams->Get('steps', []); + + // Display this step of the wizard only if there is something to display + if (count($aOptions) > 0) { + $this->aSteps[] = [ + 'title' => 'Extensions', + 'description' => '

    Select additional extensions to install. You can launch the installation again to install new extensions or remove installed ones.

    ', + 'banner' => '/images/icons/icons8-puzzle.svg', + 'options' => $aOptions, + ]; + $this->oWizard->SetParameter('additional_extensions_modules', json_encode($aOptions)); + } + } else { + // No wizard configuration provided, build a standard one with just one big list. All items are mandatory, only works when there are no conflicted modules. + $this->aSteps = [ + [ + 'title' => 'Modules Selection', + 'description' => '

    Select the modules to install. You can launch the installation again to install new modules, but you cannot remove already installed modules.

    ', + 'banner' => '/images/icons/icons8-apps-tab.svg', + 'options' => $aOptions, + ], + ]; + } + } + return $this->aSteps[$index] ?? null; + } + + public function ComputeChoiceFlags(array $aChoice, string $sChoiceId, array $aSelectedComponents, bool $bAllDisabled, bool $bDisableUninstallCheck, bool $bUpgradeMode) + { + $oITopExtension = $this->oExtensionsMap->GetFromExtensionCode($aChoice['extension_code']); + $bCanBeUninstalled = isset($aChoice['uninstallable']) ? $aChoice['uninstallable'] === true || $aChoice['uninstallable'] === 'yes' : $oITopExtension->CanBeUninstalled(); + $bSelected = isset($aSelectedComponents[$sChoiceId]) && ($aSelectedComponents[$sChoiceId] == $sChoiceId); + $bMandatory = (isset($aChoice['mandatory']) && $aChoice['mandatory']) || $bUpgradeMode && $oITopExtension->bInstalled && !$bCanBeUninstalled && !$bDisableUninstallCheck; + + $bMissingFromDisk = isset($aChoice['missing']) && $aChoice['missing'] === true; + $bInstalled = $bMissingFromDisk || $oITopExtension->bInstalled; + $bDisabled = $bMandatory || $bAllDisabled || $bMissingFromDisk; + $bChecked = $bMandatory || $bSelected; + + if (isset($aChoice['sub_options'])) { + $aOptions = $aChoice['sub_options']['options'] ?? []; + foreach ($aOptions as $index => $aSubChoice) { + $sSubChoiceId = $sChoiceId.self::$SEP.$index; + $aSubFlags = $this->ComputeChoiceFlags($aSubChoice, $sSubChoiceId, $aSelectedComponents, $bAllDisabled, $bDisableUninstallCheck, $bUpgradeMode); + if ($aSubFlags['checked']) { + $bChecked = true; + if ($aSubFlags['disabled']) { + //If some sub options are enabled and cannot be disabled, this choice should also cannot be disabled since it would disable all its sub options + $bDisabled = true; + } + } + + } + } + + return [ + 'uninstallable' => $bCanBeUninstalled, + 'missing' => $bMissingFromDisk, + 'installed' => $bInstalled, + 'disabled' => $bDisabled, + 'checked' => $bChecked, + ]; + } + + protected function DisplayOptions($oPage, $aStepInfo, $aSelectedComponents, $aDefaults, $sParentId = '', $bAllDisabled = false) + { + $aOptions = $aStepInfo['options'] ?? []; + $aAlternatives = $aStepInfo['alternatives'] ?? []; + + $bDisableUninstallCheck = (bool)$this->oWizard->GetParameter('force-uninstall', false); + + foreach ($aOptions as $index => $aChoice) { + $sChoiceId = $sParentId.self::$SEP.$index; + $sDataId = 'data-id="'.utils::EscapeHtml($aChoice['extension_code']).'"'; + $sId = utils::EscapeHtml($aChoice['extension_code']); + $aFlags = static::ComputeChoiceFlags($aChoice, $sChoiceId, $aSelectedComponents, $bAllDisabled, $bDisableUninstallCheck, $this->bUpgrade); + + $sTooltip = ''; + $sUnremovable = ''; + if ($aFlags['missing']) { + $sTooltip .= 'source removed'; + } + if ($aFlags['installed']) { + $sTooltip .= 'installed'; + $sTooltip .= 'to be uninstalled'; + } else { + $sTooltip .= 'to be installed'; + $sTooltip .= 'not installed'; + } + if (!$aFlags['uninstallable']) { + $sTooltip .= 'cannot be uninstalled'; + } + if ($aFlags['disabled'] && !$aFlags['checked'] && !$aFlags['uninstallable'] && !$bDisableUninstallCheck) { + $this->bCanMoveForward = false;//Disable "Next" + } + $sChecked = $aFlags['checked'] ? ' checked ' : ''; + $sDisabled = $aFlags['disabled'] ? ' disabled data-disabled="disabled" ' : ''; + $sMissingModule = $aFlags['missing'] ? 'setup-extension--missing' : ''; + + $sHiddenInput = $aFlags['disabled'] && $aFlags['checked'] ? '' : ''; + $oPage->add('
    '.$sHiddenInput.' '); + $this->DisplayChoice($oPage, $aChoice, $aSelectedComponents, $aDefaults, $sChoiceId, $aFlags['disabled'], $sTooltip); + $oPage->add('
    '); + } + $sChoiceName = null; + $sDisabled = ''; + $bDisabled = false; + $sChoiceIdNone = null; + foreach ($aAlternatives as $index => $aChoice) { + $sChoiceId = $sParentId.self::$SEP.$index; + if ($sChoiceName == null) { + $sChoiceName = $sChoiceId; // All radios share the same name + } + $bIsDefault = array_key_exists($sChoiceName, $aDefaults) && ($aDefaults[$sChoiceName] == $sChoiceId); + $bMandatory = (isset($aChoice['mandatory']) && $aChoice['mandatory']) || ($this->bUpgrade && $bIsDefault); + if ($bMandatory || $bAllDisabled) { + // One choice is mandatory, all alternatives are disabled + $sDisabled = ' disabled data-disabled="disabled"'; + $bDisabled = true; + } + if ((!isset($aChoice['sub_options']) || (count($aChoice['sub_options']) == 0)) && (!isset($aChoice['modules']) || (count($aChoice['modules']) == 0))) { + $sChoiceIdNone = $sChoiceId; // the "None" / empty choice + } + } + + if (!array_key_exists($sChoiceName, $aDefaults) || ($aDefaults[$sChoiceName] == $sChoiceIdNone)) { + // The "none" choice does not disable the selection !! + $sDisabled = ''; + $bDisabled = false; + } + + foreach ($aAlternatives as $index => $aChoice) { + $sAttributes = ''; + $sChoiceId = $sParentId.self::$SEP.$index; + $sDataId = 'data-id="'.utils::EscapeHtml($aChoice['extension_code']).'"'; + $sId = utils::EscapeHtml($aChoice['extension_code']); + if ($sChoiceName == null) { + $sChoiceName = $sChoiceId; // All radios share the same name + } + $bIsDefault = array_key_exists($sChoiceName, $aDefaults) && ($aDefaults[$sChoiceName] == $sChoiceId); + $bSelected = isset($aSelectedComponents[$sChoiceName]) && ($aSelectedComponents[$sChoiceName] == $sChoiceId); + if (!isset($aSelectedComponents[$sChoiceName]) && ($sChoiceIdNone != null)) { + // No choice selected, select the "None" option + $bSelected = ($sChoiceId == $sChoiceIdNone); + } + $bMandatory = (isset($aChoice['mandatory']) && $aChoice['mandatory']) || ($this->bUpgrade && $bIsDefault); + + if ($bSelected) { + $sAttributes = ' checked '; + } + $sHidden = ''; + if ($bMandatory && $bDisabled) { + $sAttributes = ' checked '; + $sHidden = ''; + } + $oPage->add('
    '.$sHidden.' '); + $this->DisplayChoice($oPage, $aChoice, $aSelectedComponents, $aDefaults, $sChoiceId, $bDisabled && !$bSelected); + $oPage->add('
    '); + } + } + + protected function DisplayChoice($oPage, $aChoice, $aSelectedComponents, $aDefaults, $sChoiceId, $bDisabled = false, $sTooltip = '') + { + $sMoreInfo = (isset($aChoice['more_info']) && ($aChoice['more_info'] != '')) ? 'More information' : ''; + $sSourceLabel = $aChoice['source_label'] ?? ''; + $sId = utils::EscapeHtml($aChoice['extension_code']); + + $oPage->add(' '.$sMoreInfo.''); + $sDescription = isset($aChoice['description']) ? utils::EscapeHtml($aChoice['description']) : ''; + $oPage->add('
    '.$sDescription.''); + if (isset($aChoice['sub_options'])) { + $this->DisplayOptions($oPage, $aChoice['sub_options'], $aSelectedComponents, $aDefaults, $sChoiceId, $bDisabled); + } + $oPage->add('
    '); + } + + protected function GetSourceFilePath() + { + $sSourceDir = $this->oWizard->GetParameter('source_dir'); + return $sSourceDir.'/installation.xml'; + } + + public function CanMoveForward() + { + return true; + } + + public function JSCanMoveForward() + { + + return $this->bCanMoveForward ? 'return true;' : 'return false;'; + } + + public function GetNextButtonLabel() + { + return $this->bCanMoveForward ? 'Next' : 'Non-uninstallable extension missing'; + } + +} diff --git a/setup/wizardsteps/WizStepSummary.php b/setup/wizardsteps/WizStepSummary.php new file mode 100644 index 000000000..f732675a9 --- /dev/null +++ b/setup/wizardsteps/WizStepSummary.php @@ -0,0 +1,236 @@ +bDependencyCheck)) { + $aSelectedModules = json_decode($this->oWizard->GetParameter('selected_modules'), true); + $this->bDependencyCheck = true; + try { + SetupUtils::AnalyzeInstallation($this->oWizard, true, $aSelectedModules); + } catch (MissingDependencyException $e) { + $this->bDependencyCheck = false; + $this->sDependencyIssue = $e->getHtmlDesc(); + } + } + return $this->bDependencyCheck; + } + + public function GetTitle() + { + $sMode = $this->oWizard->GetParameter('mode', 'install'); + if ($sMode == 'install') { + return 'Ready to install'; + + } else { + return 'Ready to upgrade'; + } + } + + public function GetPossibleSteps() + { + return ['WizStepBuild']; + } + + /** + * Returns the label for the " Next >> " button + * @return string The label for the button + */ + public function GetNextButtonLabel() + { + return 'Install'; + } + + public function CanMoveForward() + { + if ($this->CheckDependencies()) { + return true; + } else { + return false; + } + } + + public function ProcessParams($bMoveForward = true) + { + return ['class' => 'WizStepBuild', 'state' => '']; + } + + public function Display(WebPage $oPage) + { + + $aInstallParams = $this->BuildConfig(); + + $sMode = $aInstallParams['mode']; + + $sDestination = ITOP_APPLICATION.(($sMode == 'install') ? ' version '.ITOP_VERSION.' is about to be installed ' : ' is about to be upgraded '); + $sDBDescription = ' existing database '.$aInstallParams['database']['name'].''; + if (($sMode == 'install') && ($this->oWizard->GetParameter('create_db') == 'yes')) { + $sDBDescription = ' new database '.$aInstallParams['database']['name'].''; + } + $sDestination .= 'into the '.$sDBDescription.' on the server '.$aInstallParams['database']['server'].'.'; + $oPage->add('

    '.$sDestination.'

    '); + + $oPage->add('
    Installation Parameters'); + $oPage->add('
    '); + + $oPage->add('
    Extensions to be installed'); + $aExtensionsAdded = json_decode($this->oWizard->GetParameter('extensions_added'), true); + + if (count($aExtensionsAdded) > 0) { + $sExtensionsAdded = '
      '; + foreach ($aExtensionsAdded as $sExtensionCode => $sLabel) { + $sExtensionsAdded .= "
    • $sLabel
    • '"; + } + $sExtensionsAdded .= '
    '; + } else { + $sExtensionsAdded = '
    • No extension added.
    '; + } + $oPage->add($sExtensionsAdded); + $oPage->add('
    '); + $oPage->add('
    Extensions to be uninstalled'); + + $aExtensionsRemoved = json_decode($this->oWizard->GetParameter('removed_extensions'), true) ?? []; + $aExtensionsNotUninstallable = json_decode($this->oWizard->GetParameter('extensions_not_uninstallable')); + if (count($aExtensionsRemoved) > 0) { + $sExtensionsRemoved = '
      '; + foreach ($aExtensionsRemoved as $sExtensionCode => $sLabel) { + if (in_array($sExtensionCode, $aExtensionsNotUninstallable)) { + $sExtensionsRemoved .= "
    • $sLabel (forced uninstallation)
    • "; + } else { + $sExtensionsRemoved .= "
    • $sLabel
    • "; + } + } + $sExtensionsRemoved .= '
    '; + } else { + $sExtensionsRemoved = '
    • No extension removed.
    '; + } + $oPage->add($sExtensionsRemoved); + $oPage->add('
    '); + + $oPage->add('
    Database Parameters
      '); + $oPage->add('
    • Server Name: '.$aInstallParams['database']['server'].'
    • '); + $oPage->add('
    • DB User Name: '.$aInstallParams['database']['user'].'
    • '); + $oPage->add('
    • DB user password: ***
    • '); + if (($sMode == 'install') && ($this->oWizard->GetParameter('create_db') == 'yes')) { + $oPage->add('
    • Database Name: '.$aInstallParams['database']['name'].' (will be created)
    • '); + } else { + $oPage->add('
    • Database Name: '.$aInstallParams['database']['name'].'
    • '); + } + if ($aInstallParams['database']['prefix'] != '') { + $oPage->add('
    • Prefix for the '.ITOP_APPLICATION.' tables: '.$aInstallParams['database']['prefix'].'
    • '); + } else { + $oPage->add('
    • Prefix for the '.ITOP_APPLICATION.' tables: none
    • '); + } + $oPage->add('
    '); + + $oPage->add('
    Data Model Configuration'); + $oPage->add($this->oWizard->GetParameter('display_choices')); + $oPage->add('
    '); + + $oPage->add('
    Other Parameters
      '); + if ($sMode == 'install') { + $oPage->add('
    • Default language: '.$aInstallParams['language'].'
    • '); + } + + $oPage->add('
    • URL to access the application: '.$aInstallParams['url'].'
    • '); + $oPage->add('
    • Graphviz\' dot path: '.$aInstallParams['graphviz_path'].'
    • '); + if ($aInstallParams['sample_data'] == 'yes') { + $oPage->add('
    • Sample data will be loaded into the database.
    • '); + } + if ($aInstallParams['old_addon']) { + $oPage->add('
    • Compatibility mode: Using the version 1.2 of the UserRightsProfiles add-on.
    • '); + } + $oPage->add('
    '); + + if ($sMode == 'install') { + $oPage->add('
    Admininistrator Account
      '); + $oPage->add('
    • Login: '.$aInstallParams['admin_account']['user'].'
    • '); + $oPage->add('
    • Password: '.$aInstallParams['admin_account']['pwd'].'
    • '); + $oPage->add('
    • Language: '.$aInstallParams['admin_account']['language'].'
    • '); + $oPage->add('
    '); + } + + $aMiscOptions = $aInstallParams['options']; + if (count($aMiscOptions) > 0) { + $oPage->add('
    Miscellaneous Options
      '); + foreach ($aMiscOptions as $sKey => $sValue) { + $oPage->add('
    • '.$sKey.': '.$sValue.'
    • '); + } + $oPage->add('
    '); + + } + + if (isset($aMiscOptions['generate_config'])) { + $oDoc = new DOMDocument('1.0', 'UTF-8'); + $oDoc->preserveWhiteSpace = false; + $oDoc->formatOutput = true; + $oParams = new PHPParameters(); + $oParams->LoadFromHash($aInstallParams); + $oParams->ToXML($oDoc, null, 'installation'); + $sXML = $oDoc->saveXML(); + $oPage->add('
    XML Config file
      ');
      +			$oPage->add(utils::EscapeHtml($sXML));
      +			$oPage->add('
    '); + } + + $oPage->add('
    '); // params_summary + $oPage->add('
    '); + + if (!$this->CheckDependencies()) { + $oPage->error($this->sDependencyIssue); + } + + $oPage->add_ready_script( + <<oWizard->SaveParameter('application_url', ''); + $this->oWizard->SaveParameter('graphviz_path', ''); + $this->oWizard->SaveParameter('force-uninstall', false); + return ['class' => 'WizStepModulesChoice', 'state' => 'start_upgrade']; + } + + public function Display(WebPage $oPage) + { + $sApplicationURL = $this->oWizard->GetParameter('application_url', utils::GetAbsoluteUrlAppRoot(true)); //Preserve existing configuration (except for the str_replace based joker $SERVER_NAME$ which is lost) + $sDefaultGraphvizPath = (strtolower(substr(PHP_OS, 0, 3)) === 'win') ? 'C:\\Program Files\\Graphviz\\bin\\dot.exe' : '/usr/bin/dot'; + $sGraphvizPath = $this->oWizard->GetParameter('graphviz_path', $sDefaultGraphvizPath); + $oPage->add('

    Additional parameters

    '); + $oPage->add('
    '); + $oPage->add('Application URL'); + $oPage->add(''); + $oPage->add(''); + $oPage->add('
    URL:
    '); + $oPage->add('
    Change the value above if the end-users will be accessing the application by another path due to a specific configuration of the web server.
    '); + $oPage->add('
    '); + $oPage->add('
    '); + $oPage->add('Path to Graphviz\' dot application'); + $oPage->add(''); + $oPage->add(''); + $oPage->add(''); + $oPage->add('
    Path:
    '); + $oPage->add(''); + $oPage->add('
    '); + $sAuthentToken = $this->oWizard->GetParameter('authent', ''); + $oPage->add(''); + $oPage->add_ready_script( + <<AddUseSymlinksFlagOption($oPage); + $this->AddForceUninstallFlagOption($oPage); + } + + public function AsyncAction(WebPage $oPage, $sCode, $aParameters) + { + switch ($sCode) { + case 'check_graphviz': + $sGraphvizPath = $aParameters['graphviz_path']; + $aCheck = SetupUtils::CheckGraphviz($sGraphvizPath); + + // N°2214 logging TRACE results + $aTraceCheck = CheckResult::FilterCheckResultArray($aCheck, [CheckResult::TRACE]); + foreach ($aTraceCheck as $oTraceCheck) { + SetupLog::Ok($oTraceCheck->sLabel); + } + + $aNonTraceCheck = array_diff($aCheck, $aTraceCheck); + foreach ($aNonTraceCheck as $oCheck) { + switch ($oCheck->iSeverity) { + case CheckResult::INFO: + $sStatus = 'ok'; + $sInfoExplanation = $oCheck->sLabel; + $sMessage = json_encode('
    '.$sInfoExplanation.'
    '); + break; + + default: + case CheckResult::ERROR: + case CheckResult::WARNING: + $sStatus = 'ko'; + $sErrorExplanation = $oCheck->sLabel; + $sMessage = json_encode('
    '.$sErrorExplanation.'
    '); + break; + } + $oPage->add_ready_script( + <<'); + } + else + { + $("#v_application_url").html(''); + } + bGraphviz = ($('#graphviz_path').val() != ''); + if (!bGraphviz) + { + // Does not prevent to move forward + $("#v_graphviz_path").html(''); + } + else + { + $("#v_graphviz_path").html(''); + } + return bRet; +EOF + ; + } +} diff --git a/setup/wizardsteps/WizStepWelcome.php b/setup/wizardsteps/WizStepWelcome.php new file mode 100644 index 000000000..1cd8ff532 --- /dev/null +++ b/setup/wizardsteps/WizStepWelcome.php @@ -0,0 +1,137 @@ +> " button + * @return string The label for the button + */ + public function GetNextButtonLabel() + { + return 'Continue'; + } + + public function GetPossibleSteps() + { + return ['WizStepInstallOrUpgrade']; + } + + public function ProcessParams($bMoveForward = true) + { + $sUID = SetupUtils::CreateSetupToken(); + $this->oWizard->SetParameter('authent', $sUID); + return ['class' => 'WizStepInstallOrUpgrade', 'state' => '']; + } + + public function Display(WebPage $oPage) + { + // Store the misc_options for the future... + $aMiscOptions = utils::ReadParam('option', [], false, 'raw_data'); + $sMiscOptions = $this->oWizard->GetParameter('misc_options', json_encode($aMiscOptions)); + $this->oWizard->SetParameter('misc_options', $sMiscOptions); + + $oPage->add(""); + $oPage->add_ready_script( + << 0) + { + alert("Internet Explorer version 10 or older is NOT supported! (Check that IE is not running in compatibility mode)"); + } +EOF + ); + $oPage->add('

    '.ITOP_APPLICATION.' Installation Wizard

    '); + $aResults = SetupUtils::CheckPhpAndExtensions(); + $this->bCanMoveForward = true; + $aInfo = []; + $aWarnings = []; + $aErrors = []; + foreach ($aResults as $oCheckResult) { + switch ($oCheckResult->iSeverity) { + case CheckResult::ERROR: + $aErrors[] = $oCheckResult->sLabel; + $this->bCanMoveForward = false; + break; + + case CheckResult::WARNING: + $aWarnings[] = $oCheckResult->sLabel; + break; + + case CheckResult::INFO: + $aInfo[] = $oCheckResult->sLabel; + break; + + case CheckResult::TRACE: + SetupLog::Ok($oCheckResult->sLabel); + break; + } + } + $sStyle = 'style="display:none;overflow:auto;"'; + $sToggleButtons = ''; + if (count($aErrors) > 0) { + $sStyle = 'overflow:auto;"'; + $sTitle = count($aErrors).' Error(s), '.count($aWarnings).' Warning(s).'; + $sH2Class = 'text-error'; + } elseif (count($aWarnings) > 0) { + $sTitle = count($aWarnings).' Warning(s) '.$sToggleButtons; + $sH2Class = 'text-warning'; + } else { + $sTitle = 'Ok. '.$sToggleButtons; + $sH2Class = 'text-valid'; + } + $oPage->add( + <<Prerequisites validation: $sTitle +
    +HTML + ); + foreach ($aErrors as $sText) { + $oPage->error($sText); + } + foreach ($aWarnings as $sText) { + $oPage->warning($sText); + } + foreach ($aInfo as $sText) { + $oPage->ok($sText); + } + $oPage->add('
    '); + if (!$this->bCanMoveForward) { + $oPage->p('Sorry, the installation cannot continue. Please fix the errors and reload this page to launch the installation again.'); + $oPage->p(''); + } + $oPage->add_ready_script('CheckDirectoryConfFilesPermissions("'.utils::GetItopVersionWikiSyntax().'")'); + } + + public function CanMoveForward() + { + return $this->bCanMoveForward; + } +} diff --git a/setup/wizardsteps/WizardStep.php b/setup/wizardsteps/WizardStep.php new file mode 100644 index 000000000..97bc5e7b8 --- /dev/null +++ b/setup/wizardsteps/WizardStep.php @@ -0,0 +1,351 @@ + + * WizStepLicense WizStepDetectedInfo + * WizStepDBParams + + + * WizStepAdminAccount | | + * WizStepInstallMiscParams v +------> + * + WizStepLicense2 +--> WizStepUpgradeMiscParams + * | + + * +---> <-----------------------------------+ + * WizStepModulesChoice + * WizStepSummary + * WizStepDone + */ + +use Combodo\iTop\Application\WebPage\WebPage; + +/** + * Abstract class to build "steps" for the wizard controller + * If a step needs to maintain an internal "state" (for complex steps) + * then it's up to the derived class to implement the behavior based on + * the internal 'sCurrentState' variable. + * @copyright Copyright (C) 2010-2024 Combodo SAS + * @license http://opensource.org/licenses/AGPL-3.0 + */ +abstract class WizardStep +{ + /** + * A reference to the WizardController + * @var WizardController + */ + protected $oWizard; + /** + * Current 'state' of the wizard step. Simple 'steps' can ignore it + * @var string + */ + protected $sCurrentState; + + public function __construct(WizardController $oWizard, $sCurrentState) + { + $this->oWizard = $oWizard; + $this->sCurrentState = $sCurrentState; + } + + public function GetState() + { + return $this->sCurrentState; + } + + /** + * Displays the wizard page for the current class/state + * The page can contain any number of "" fields, but no "
    ...
    " tag + * The name of the input fields (and their id if one is supplied) MUST NOT start with "_" + * (this is reserved for the wizard's own parameters) + * @return void + */ + abstract public function Display(WebPage $oPage); + + /** + * Processes the page's parameters and (if moving forward) returns the next step/state to be displayed + * @param bool $bMoveForward True if the wizard is moving forward 'Next >>' button pressed, false otherwise + * @return hash array('class' => $sNextClass, 'state' => $sNextState) + */ + abstract public function ProcessParams($bMoveForward = true); + + /** + * Returns the list of possible steps from this step forward + * @return array Array of strings (step classes) + */ + abstract public function GetPossibleSteps(); + + /** + * Returns title of the current step + * @return string The title of the wizard page for the current step + */ + abstract public function GetTitle(); + + /** + * Tells whether the parameters are Ok to move forward + * @return boolean True to move forward, false to stey on the same step + */ + public function ValidateParams() + { + return true; + } + + /** + * Tells whether this step/state is the last one of the wizard (dead-end) + * @return boolean True if the 'Next >>' button should be displayed + */ + public function CanMoveForward() + { + return true; + } + + /** + * Tells whether the "Next" button should be enabled interactively + * @return string A piece of javascript code returning either true or false + */ + public function JSCanMoveForward() + { + return 'return true;'; + } + + /** + * Returns the label for the " Next >> " button + * @return string The label for the button + */ + public function GetNextButtonLabel() + { + return 'Next'; + } + + /** + * Tells whether this step/state allows to go back or not + * @return boolean True if the '<< Back' button should be displayed + */ + public function CanMoveBackward() + { + return true; + } + + /** + * Tells whether the "Back" button should be enabled interactively + * @return string A piece of javascript code returning either true or false + */ + public function JSCanMoveBackward() + { + 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) + * @param hash $aParameters The action's parameters name => value + */ + public function AsyncAction(WebPage $oPage, $sCode, $aParameters) + { + } +} + +/* + * Example of a simple Setup Wizard with some parameters to store + * the installation mode (install | upgrade) and a simple asynchronous + * (AJAX) action. + * + * The setup wizard is executed by the following code: + * + * $oWizard = new WizardController('Step1'); + * $oWizard->Run(); + * +class Step1 extends WizardStep +{ + public function GetTitle() + { + return 'Welcome'; + } + + public function GetPossibleSteps() + { + return array('Step2', 'Step2bis'); + } + + public function ProcessParams($bMoveForward = true) + { + $sNextStep = ''; + $sInstallMode = utils::ReadParam('install_mode'); + if ($sInstallMode == 'install') + { + $this->oWizard->SetParameter('install_mode', 'install'); + $sNextStep = 'Step2'; + } + else + { + $this->oWizard->SetParameter('install_mode', 'upgrade'); + $sNextStep = 'Step2bis'; + + } + return array('class' => $sNextStep, 'state' => ''); + } + + public function Display(WebPage $oPage) + { + $oPage->p('This is Step 1!'); + $sInstallMode = $this->oWizard->GetParameter('install_mode', 'install'); + $sChecked = ($sInstallMode == 'install') ? ' checked ' : ''; + $oPage->p(' Install'); + $sChecked = ($sInstallMode == 'upgrade') ? ' checked ' : ''; + $oPage->p(' Upgrade'); + } +} + +class Step2 extends WizardStep +{ + public function GetTitle() + { + return 'Installation Parameters'; + } + + public function GetPossibleSteps() + { + return array('Step3'); + } + + public function ProcessParams($bMoveForward = true) + { + return array('class' => 'Step3', 'state' => ''); + } + + public function Display(WebPage $oPage) + { + $oPage->p('This is Step 2! (Installation)'); + } +} + +class Step2bis extends WizardStep +{ + public function GetTitle() + { + return 'Upgrade Parameters'; + } + + public function GetPossibleSteps() + { + return array('Step2ter'); + } + + public function ProcessParams($bMoveForward = true) + { + $sUpgradeInfo = utils::ReadParam('upgrade_info'); + $this->oWizard->SetParameter('upgrade_info', $sUpgradeInfo); + $sAdditionalUpgradeInfo = utils::ReadParam('additional_upgrade_info'); + $this->oWizard->SetParameter('additional_upgrade_info', $sAdditionalUpgradeInfo); + return array('class' => 'Step2ter', 'state' => ''); + } + + public function Display(WebPage $oPage) + { + $oPage->p('This is Step 2bis! (Upgrade)'); + $sUpgradeInfo = $this->oWizard->GetParameter('upgrade_info', ''); + $oPage->p('Type your name here: '); + $sAdditionalUpgradeInfo = $this->oWizard->GetParameter('additional_upgrade_info', ''); + $oPage->p('The installer replies: '); + + $oPage->add_ready_script("$('#upgrade_info').change(function() { + $('#v_upgrade_info').html(''); + WizardAsyncAction('', { upgrade_info: $('#upgrade_info').val() }); });"); + } + + public function AsyncAction(WebPage $oPage, $sCode, $aParameters) + { + usleep(300000); // 300 ms + $sName = $aParameters['upgrade_info']; + $sReply = addslashes("Hello ".$sName); + + $oPage->add_ready_script( +<< 'Step3', 'state' => ''); + } + + public function Display(WebPage $oPage) + { + $oPage->p('This is Step 2ter! (Upgrade)'); + } +} + +class Step3 extends WizardStep +{ + public function GetTitle() + { + return 'Installation Complete'; + } + + public function GetPossibleSteps() + { + return array(); + } + + public function ProcessParams($bMoveForward = true) + { + return array('class' => '', 'state' => ''); + } + + public function Display(WebPage $oPage) + { + $oPage->p('This is the FINAL Step'); + } + + public function CanMoveForward() + { + return false; + } +} + +End of the example */