mirror of
https://github.com/Combodo/iTop.git
synced 2026-07-16 11:46:38 +02:00
Compare commits
14 Commits
issue/9746
...
feature/97
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f032710d7e | ||
|
|
6b396adeea | ||
|
|
c448f8411c | ||
|
|
4b4fe55060 | ||
|
|
dedcd4af19 | ||
|
|
0556a03839 | ||
|
|
64df429715 | ||
|
|
9ae813db2b | ||
|
|
768423a6aa | ||
|
|
921e7c88fe | ||
|
|
d6ef4fb7bb | ||
|
|
a335e1004b | ||
|
|
e882df96d9 | ||
|
|
1d9e7f9b55 |
5
.github/pull_request_template.md
vendored
5
.github/pull_request_template.md
vendored
@@ -9,11 +9,6 @@ Any PRs not following the guidelines or with missing information will not be con
|
||||
|
||||
## Base information
|
||||
|
||||
| Question | Answer
|
||||
|----------------------------------------------------------------|--------
|
||||
| Related to a SourceForge thread / Another PR / A GitHub Issue / Combodo ticket? | <!-- Put the URL --> |
|
||||
| Type of change? | Bug fix / Enhancement / Translations
|
||||
|
||||
| Question | Answer |
|
||||
|---------------------------------------------------------------------------------|--------------------------------------|
|
||||
| Related to a SourceForge thread / Another PR / A GitHub Issue / Combodo ticket? | <!-- Put the URL --> |
|
||||
|
||||
@@ -1935,9 +1935,8 @@ class DBObjectSearch extends DBSearch
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
* @return DBObjectSearch
|
||||
*/
|
||||
protected function ApplyDataFilters(): DBObjectSearch
|
||||
protected function ApplyDataFilters(): DBSearch
|
||||
{
|
||||
if ($this->IsAllDataAllowed() || $this->IsDataFiltered()) {
|
||||
return $this;
|
||||
|
||||
@@ -676,9 +676,8 @@ class DBUnionSearch extends DBSearch
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
* @return DBUnionSearch
|
||||
*/
|
||||
protected function ApplyDataFilters(): DBUnionSearch
|
||||
protected function ApplyDataFilters(): DBSearch
|
||||
{
|
||||
if ($this->IsAllDataAllowed() || $this->IsDataFiltered()) {
|
||||
return $this;
|
||||
|
||||
@@ -614,6 +614,13 @@ class LogChannels
|
||||
* @since 3.2.0
|
||||
*/
|
||||
public const SECURITY = 'Security';
|
||||
|
||||
/**
|
||||
* For Session parameters
|
||||
*
|
||||
* @Since 3.3.0
|
||||
*/
|
||||
public const SESSION_PARAMETERS = 'SessionParameters';
|
||||
}
|
||||
|
||||
abstract class LogAPI
|
||||
|
||||
@@ -5761,36 +5761,37 @@ abstract class MetaModel
|
||||
|
||||
self::$m_sEnvironment = $sEnvironment;
|
||||
|
||||
try {
|
||||
if (!defined('MODULESROOT')) {
|
||||
define('MODULESROOT', APPROOT.'env-'.self::$m_sEnvironment.'/');
|
||||
if (!defined('MODULESROOT')) {
|
||||
define('MODULESROOT', APPROOT.'env-'.self::$m_sEnvironment.'/');
|
||||
|
||||
self::$m_bTraceSourceFiles = $bTraceSourceFiles;
|
||||
self::$m_bTraceSourceFiles = $bTraceSourceFiles;
|
||||
|
||||
// $config can be either a filename, or a Configuration object (volatile!)
|
||||
if ($config instanceof Config) {
|
||||
self::LoadConfig($config, $bAllowCache);
|
||||
} else {
|
||||
self::LoadConfig(new Config($config), $bAllowCache);
|
||||
}
|
||||
|
||||
if ($bModelOnly) {
|
||||
return;
|
||||
}
|
||||
// $config can be either a filename, or a Configuration object (volatile!)
|
||||
if ($config instanceof Config) {
|
||||
self::LoadConfig($config, $bAllowCache);
|
||||
} else {
|
||||
self::LoadConfig(new Config($config), $bAllowCache);
|
||||
}
|
||||
|
||||
CMDBSource::SelectDB(self::$m_sDBName);
|
||||
|
||||
foreach (MetaModel::EnumPlugins('ModuleHandlerApiInterface') as $oPHPClass) {
|
||||
$oPHPClass::OnMetaModelStarted();
|
||||
if ($bModelOnly) {
|
||||
// Event service must be initialized after the MetaModel startup, otherwise it cannot discover classes implementing the iEventServiceSetup interface
|
||||
EventService::InitService();
|
||||
EventService::FireEvent(new EventData(ApplicationEvents::APPLICATION_EVENT_METAMODEL_STARTED));
|
||||
return;
|
||||
}
|
||||
|
||||
ExpressionCache::Warmup();
|
||||
} finally {
|
||||
// Event service must be initialized after the MetaModel startup, otherwise it cannot discover classes implementing the iEventServiceSetup interface
|
||||
EventService::InitService();
|
||||
EventService::FireEvent(new EventData(ApplicationEvents::APPLICATION_EVENT_METAMODEL_STARTED));
|
||||
}
|
||||
|
||||
CMDBSource::SelectDB(self::$m_sDBName);
|
||||
|
||||
foreach (MetaModel::EnumPlugins('ModuleHandlerApiInterface') as $oPHPClass) {
|
||||
$oPHPClass::OnMetaModelStarted();
|
||||
}
|
||||
|
||||
ExpressionCache::Warmup();
|
||||
|
||||
// Event service must be initialized after the MetaModel startup, otherwise it cannot discover classes implementing the iEventServiceSetup interface
|
||||
EventService::InitService();
|
||||
EventService::FireEvent(new EventData(ApplicationEvents::APPLICATION_EVENT_METAMODEL_STARTED));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -19,6 +19,7 @@ use Combodo\iTop\DataFeatureRemoval\Helper\DataFeatureRemovalLog;
|
||||
use Combodo\iTop\DataFeatureRemoval\Service\DataCleanupService;
|
||||
use Combodo\iTop\DataFeatureRemoval\Service\DataFeatureRemoverExtensionService;
|
||||
use Combodo\iTop\DataFeatureRemoval\Service\StaticDeletionPlan;
|
||||
use Combodo\iTop\Service\Session\SessionParameters;
|
||||
use Combodo\iTop\Setup\FeatureRemoval\DryRemovalRuntimeEnvironment;
|
||||
use Combodo\iTop\Setup\FeatureRemoval\SetupAudit;
|
||||
use ContextTag;
|
||||
@@ -47,6 +48,9 @@ class DataFeatureRemovalController extends Controller
|
||||
{
|
||||
$aParams = [];
|
||||
|
||||
SetupUtils::EraseSetupToken();
|
||||
(new SessionParameters(SetupUtils::SESSION_PARAMETERS_NAME))->Erase();
|
||||
|
||||
$this->AddAnalyzeParams();
|
||||
$aParams['sTransactionId'] = utils::GetNewTransactionId();
|
||||
$aParams['iColumnCount'] = $this->iColumnCount;
|
||||
@@ -180,7 +184,7 @@ class DataFeatureRemovalController extends Controller
|
||||
];
|
||||
|
||||
foreach ($aHiddenInputs as $sInputName => $sInputValue) {
|
||||
$aParams['aSetupParams']["_params[$sInputName]"] = $sInputValue;
|
||||
$aParams['aSetupParams'][$sInputName] = $sInputValue;
|
||||
}
|
||||
|
||||
[$aParams['aDeletionPlanSummary'], $aParams['iQueryCount'], $aParams['bDeletionPossible']] = $this->GetDeletionPlanSummaryTable($aGetRemovedClasses);
|
||||
@@ -189,6 +193,7 @@ class DataFeatureRemovalController extends Controller
|
||||
Session::Set('aDeletionExecutionSummary', serialize($this->aDeletionExecutionSummary));
|
||||
|
||||
if (!$aParams['bDeletionNeeded']) {
|
||||
// Erase session setup parameters
|
||||
SetupUtils::CreateSetupToken();
|
||||
}
|
||||
|
||||
@@ -206,12 +211,13 @@ class DataFeatureRemovalController extends Controller
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $aAddedExtensions
|
||||
* @param array $aRemovedExtensions
|
||||
* @param bool $bForceCompilation
|
||||
* @return void
|
||||
* @throws \ConfigException
|
||||
* @throws \CoreException
|
||||
* @param array $aAddedExtensions
|
||||
* @param array $aRemovedExtensions
|
||||
* @param bool $bForceCompilation
|
||||
*
|
||||
* @return void
|
||||
* @throws \ConfigException
|
||||
* @throws \CoreException
|
||||
*/
|
||||
private function Compile(array $aAddedExtensions, array $aRemovedExtensions, bool $bForceCompilation = true): void
|
||||
{
|
||||
|
||||
@@ -28,7 +28,7 @@ SetupWebPage::AddModule(
|
||||
'category' => 'Portal',
|
||||
// Setup
|
||||
'dependencies' => [
|
||||
'itop-attachments/3.2.1', //CMDBChangeOpAttachmentRemoved
|
||||
'itop-attachments/3.2.1||true', //CMDBChangeOpAttachmentRemoved
|
||||
],
|
||||
'mandatory' => true,
|
||||
'visible' => false,
|
||||
|
||||
@@ -476,8 +476,7 @@ $(function()
|
||||
|
||||
let sStimulusCode = (undefined !== oData.stimulus_code) ? oData.stimulus_code : null
|
||||
// If several entry forms filled, show a confirmation message
|
||||
if ((GetUserPreference('activity_panel.show_multiple_entries_submit_confirmation',true) === true
|
||||
|| GetUserPreference('activity_panel.show_multiple_entries_submit_confirmation', true) === "true")
|
||||
if (GetUserPreferenceAsBoolean('activity_panel.show_multiple_entries_submit_confirmation',true) === true
|
||||
&& (Object.keys(await this._GetEntriesFromAllForms()).length > 1)) {
|
||||
this._ShowEntriesSubmitConfirmation(sStimulusCode);
|
||||
}
|
||||
|
||||
10
js/utils.js
10
js/utils.js
@@ -135,14 +135,10 @@ function SetUserPreference(sPreferenceCode, sPrefValue, bPersistent) {
|
||||
} catch (err) {
|
||||
sPreviousValue = undefined;
|
||||
}
|
||||
oUserPreferences[sPreferenceCode] = sPrefValue;
|
||||
if (bPersistent && (sPrefValue != sPreviousValue)) {
|
||||
return $.post(GetAbsoluteUrlAppRoot()+'pages/ajax.render.php',
|
||||
{operation: 'set_pref', code: sPreferenceCode, value: sPrefValue}, function (data) {
|
||||
}).done(function() {
|
||||
oUserPreferences[sPreferenceCode] = sPrefValue;
|
||||
}); // Make it persistent
|
||||
} else {
|
||||
oUserPreferences[sPreferenceCode] = sPrefValue;
|
||||
ajax_request = $.post(GetAbsoluteUrlAppRoot()+'pages/ajax.render.php',
|
||||
{operation: 'set_pref', code: sPreferenceCode, value: sPrefValue}); // Make it persistent
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,10 +14,7 @@ if (PHP_VERSION_ID < 50600) {
|
||||
echo $err;
|
||||
}
|
||||
}
|
||||
trigger_error(
|
||||
$err,
|
||||
E_USER_ERROR
|
||||
);
|
||||
throw new RuntimeException($err);
|
||||
}
|
||||
|
||||
require_once __DIR__ . '/composer/autoload_real.php';
|
||||
|
||||
@@ -137,7 +137,6 @@ return array(
|
||||
'Combodo\\iTop\\Application\\Helper\\FormHelper' => $baseDir . '/sources/Application/Helper/FormHelper.php',
|
||||
'Combodo\\iTop\\Application\\Helper\\ImportHelper' => $baseDir . '/sources/Application/Helper/ImportHelper.php',
|
||||
'Combodo\\iTop\\Application\\Helper\\SearchHelper' => $baseDir . '/sources/Application/Helper/SearchHelper.php',
|
||||
'Combodo\\iTop\\Application\\Helper\\Session' => $baseDir . '/sources/Application/Helper/Session.php',
|
||||
'Combodo\\iTop\\Application\\Helper\\SynchroReplicaHelper' => $baseDir . '/sources/Application/Helper/SynchroReplicaHelper.php',
|
||||
'Combodo\\iTop\\Application\\Helper\\WebResourcesHelper' => $baseDir . '/sources/Application/Helper/WebResourcesHelper.php',
|
||||
'Combodo\\iTop\\Application\\Newsroom\\iTopNewsroomProvider' => $baseDir . '/sources/Application/Newsroom/iTopNewsroomProvider.php',
|
||||
@@ -640,6 +639,8 @@ return array(
|
||||
'Combodo\\iTop\\Service\\Router\\Exception\\RouteNotFoundException' => $baseDir . '/sources/Service/Router/Exception/RouteNotFoundException.php',
|
||||
'Combodo\\iTop\\Service\\Router\\Exception\\RouterException' => $baseDir . '/sources/Service/Router/Exception/RouterException.php',
|
||||
'Combodo\\iTop\\Service\\Router\\Router' => $baseDir . '/sources/Service/Router/Router.php',
|
||||
'Combodo\\iTop\\Service\\Session\\Session' => $baseDir . '/sources/Service/Session/Session.php',
|
||||
'Combodo\\iTop\\Service\\Session\\SessionParameters' => $baseDir . '/sources/Service/Session/SessionParameters.php',
|
||||
'Combodo\\iTop\\Service\\Startup\\StartupService' => $baseDir . '/sources/Service/Startup/StartupService.php',
|
||||
'Combodo\\iTop\\Service\\SummaryCard\\SummaryCardService' => $baseDir . '/sources/Service/SummaryCard/SummaryCardService.php',
|
||||
'Combodo\\iTop\\Service\\TemporaryObjects\\TemporaryObjectConfig' => $baseDir . '/sources/Service/TemporaryObjects/TemporaryObjectConfig.php',
|
||||
@@ -1423,6 +1424,7 @@ return array(
|
||||
'ReportValue' => $baseDir . '/core/bulkchange.class.inc.php',
|
||||
'RestDelete' => $baseDir . '/core/restservices.class.inc.php',
|
||||
'RestResult' => $baseDir . '/application/applicationextension/rest/RestResult.php',
|
||||
'RestResultWithObjectSets' => $baseDir . '/core/restservices.class.inc.php',
|
||||
'RestResultWithObjects' => $baseDir . '/core/restservices.class.inc.php',
|
||||
'RestResultWithRelations' => $baseDir . '/core/restservices.class.inc.php',
|
||||
'RestUtils' => $baseDir . '/application/applicationextension/rest/RestUtils.php',
|
||||
|
||||
@@ -538,7 +538,6 @@ class ComposerStaticInitfc0e9e9dea11dcbb6272414776c30685
|
||||
'Combodo\\iTop\\Application\\Helper\\FormHelper' => __DIR__ . '/../..' . '/sources/Application/Helper/FormHelper.php',
|
||||
'Combodo\\iTop\\Application\\Helper\\ImportHelper' => __DIR__ . '/../..' . '/sources/Application/Helper/ImportHelper.php',
|
||||
'Combodo\\iTop\\Application\\Helper\\SearchHelper' => __DIR__ . '/../..' . '/sources/Application/Helper/SearchHelper.php',
|
||||
'Combodo\\iTop\\Application\\Helper\\Session' => __DIR__ . '/../..' . '/sources/Application/Helper/Session.php',
|
||||
'Combodo\\iTop\\Application\\Helper\\SynchroReplicaHelper' => __DIR__ . '/../..' . '/sources/Application/Helper/SynchroReplicaHelper.php',
|
||||
'Combodo\\iTop\\Application\\Helper\\WebResourcesHelper' => __DIR__ . '/../..' . '/sources/Application/Helper/WebResourcesHelper.php',
|
||||
'Combodo\\iTop\\Application\\Newsroom\\iTopNewsroomProvider' => __DIR__ . '/../..' . '/sources/Application/Newsroom/iTopNewsroomProvider.php',
|
||||
@@ -1041,6 +1040,8 @@ class ComposerStaticInitfc0e9e9dea11dcbb6272414776c30685
|
||||
'Combodo\\iTop\\Service\\Router\\Exception\\RouteNotFoundException' => __DIR__ . '/../..' . '/sources/Service/Router/Exception/RouteNotFoundException.php',
|
||||
'Combodo\\iTop\\Service\\Router\\Exception\\RouterException' => __DIR__ . '/../..' . '/sources/Service/Router/Exception/RouterException.php',
|
||||
'Combodo\\iTop\\Service\\Router\\Router' => __DIR__ . '/../..' . '/sources/Service/Router/Router.php',
|
||||
'Combodo\\iTop\\Service\\Session\\Session' => __DIR__ . '/../..' . '/sources/Service/Session/Session.php',
|
||||
'Combodo\\iTop\\Service\\Session\\SessionParameters' => __DIR__ . '/../..' . '/sources/Service/Session/SessionParameters.php',
|
||||
'Combodo\\iTop\\Service\\Startup\\StartupService' => __DIR__ . '/../..' . '/sources/Service/Startup/StartupService.php',
|
||||
'Combodo\\iTop\\Service\\SummaryCard\\SummaryCardService' => __DIR__ . '/../..' . '/sources/Service/SummaryCard/SummaryCardService.php',
|
||||
'Combodo\\iTop\\Service\\TemporaryObjects\\TemporaryObjectConfig' => __DIR__ . '/../..' . '/sources/Service/TemporaryObjects/TemporaryObjectConfig.php',
|
||||
@@ -1824,6 +1825,7 @@ class ComposerStaticInitfc0e9e9dea11dcbb6272414776c30685
|
||||
'ReportValue' => __DIR__ . '/../..' . '/core/bulkchange.class.inc.php',
|
||||
'RestDelete' => __DIR__ . '/../..' . '/core/restservices.class.inc.php',
|
||||
'RestResult' => __DIR__ . '/../..' . '/application/applicationextension/rest/RestResult.php',
|
||||
'RestResultWithObjectSets' => __DIR__ . '/../..' . '/core/restservices.class.inc.php',
|
||||
'RestResultWithObjects' => __DIR__ . '/../..' . '/core/restservices.class.inc.php',
|
||||
'RestResultWithRelations' => __DIR__ . '/../..' . '/core/restservices.class.inc.php',
|
||||
'RestUtils' => __DIR__ . '/../..' . '/application/applicationextension/rest/RestUtils.php',
|
||||
|
||||
@@ -36,8 +36,7 @@ if ($issues) {
|
||||
echo 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . str_replace('You are running '.PHP_VERSION.'.', '', implode(PHP_EOL, $issues)) . PHP_EOL.PHP_EOL;
|
||||
}
|
||||
}
|
||||
trigger_error(
|
||||
'Composer detected issues in your platform: ' . implode(' ', $issues),
|
||||
E_USER_ERROR
|
||||
throw new \RuntimeException(
|
||||
'Composer detected issues in your platform: ' . implode(' ', $issues)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ use Combodo\iTop\Application\WelcomePopup\WelcomePopupService;
|
||||
use Combodo\iTop\Controller\Base\Layout\ObjectController;
|
||||
use Combodo\iTop\Controller\WelcomePopupController;
|
||||
use Combodo\iTop\Service\Router\Router;
|
||||
use Combodo\iTop\Service\Session\SessionParameters;
|
||||
|
||||
/**
|
||||
* Displays a popup welcome message, once per session at maximum
|
||||
@@ -1306,6 +1307,10 @@ try {
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
default: // Menu node rendering (templates)
|
||||
// Erase setup parameters
|
||||
SetupUtils::EraseSetupToken();
|
||||
(new SessionParameters(SetupUtils::SESSION_PARAMETERS_NAME))->Erase();
|
||||
|
||||
ApplicationMenu::LoadAdditionalMenus();
|
||||
$oMenuNode = ApplicationMenu::GetMenuNode(ApplicationMenu::GetMenuIndexById(ApplicationMenu::GetActiveNodeId()));
|
||||
if (is_object($oMenuNode)) {
|
||||
|
||||
@@ -444,7 +444,7 @@ EOF;
|
||||
$sPwd = $oConfig->Get('db_pwd');
|
||||
$sSource = $oConfig->Get('db_name');
|
||||
$sTlsEnabled = $oConfig->Get('db_tls.enabled');
|
||||
$sTlsCA = $oConfig->Get('db_tls.ca');
|
||||
$sTlsCA = $oConfig->Get('db_tls.ca') ?? '';
|
||||
|
||||
try {
|
||||
$oMysqli = CMDBSource::GetMysqliInstance(
|
||||
|
||||
@@ -2,11 +2,10 @@
|
||||
|
||||
namespace Combodo\iTop\Setup\FeatureRemoval;
|
||||
|
||||
use ContextTag;
|
||||
use CoreException;
|
||||
use Exception;
|
||||
use IssueLog;
|
||||
use SetupLog;
|
||||
use SetupUtils;
|
||||
use utils;
|
||||
|
||||
class ModelReflectionSerializer
|
||||
@@ -15,6 +14,7 @@ class ModelReflectionSerializer
|
||||
|
||||
protected function __construct()
|
||||
{
|
||||
SetupLog::Enable(APPROOT.'log/setup.log');
|
||||
}
|
||||
|
||||
final public static function GetInstance(): ModelReflectionSerializer
|
||||
@@ -31,42 +31,53 @@ class ModelReflectionSerializer
|
||||
self::$oInstance = $oInstance;
|
||||
}
|
||||
|
||||
public const ERROR_LABEL = "Data consistency check failed: %s";
|
||||
|
||||
public function GetModelFromEnvironment(string $sEnv): array
|
||||
{
|
||||
IssueLog::Debug(__METHOD__, null, ['env' => $sEnv]);
|
||||
|
||||
$sPHPExec = trim(utils::GetConfig()->Get('php_path'));
|
||||
$sOutput = "";
|
||||
$iRes = 0;
|
||||
SetupUtils::CheckCliPhpVersionIsOk(self::ERROR_LABEL);
|
||||
|
||||
$sCommandLine = sprintf("$sPHPExec %s/get_model_reflection.php --env=%s", __DIR__, escapeshellarg($sEnv));
|
||||
exec($sCommandLine, $sOutput, $iRes);
|
||||
if ($iRes != 0) {
|
||||
$this->LogErrorWithProperLogger("Cannot get classes", null, ['env' => $sEnv, 'code' => $iRes, "output" => $sOutput, 'cmd' => $sCommandLine]);
|
||||
throw new CoreException("Cannot get classes from env ".$sEnv);
|
||||
//preliminary check
|
||||
$sEnvDir = APPROOT."env-$sEnv";
|
||||
if (! is_dir($sEnvDir)) {
|
||||
$sMsg = sprintf(self::ERROR_LABEL, "Missing environment ($sEnvDir)");
|
||||
SetupLog::Error($sMsg);
|
||||
throw new CoreException($sMsg);
|
||||
}
|
||||
|
||||
$aClasses = json_decode($sOutput[0] ?? null, true);
|
||||
$sConfigFile = APPROOT."conf/$sEnv/config-itop.php";
|
||||
if (! is_file($sConfigFile)) {
|
||||
$sMsg = sprintf(self::ERROR_LABEL, "Missing configuration ($sConfigFile)");
|
||||
SetupLog::Error($sMsg);
|
||||
throw new CoreException($sMsg);
|
||||
}
|
||||
|
||||
$sPHPExec = trim(utils::GetConfig()->Get('php_path'));
|
||||
$aOutput = null;
|
||||
$iRes = 0;
|
||||
$sCommandLine = sprintf("$sPHPExec %s/get_model_reflection.php --env=%s", __DIR__, escapeshellarg($sEnv));
|
||||
exec($sCommandLine, $aOutput, $iRes);
|
||||
if ($iRes != 0) {
|
||||
$sError = $aOutput[0] ?? 'Invalid output when serializing model';
|
||||
SetupLog::Error(sprintf(self::ERROR_LABEL, '(cli error) '.$sError), null, ['env' => $sEnv, 'code' => $iRes, "output" => $aOutput, 'cmd' => $sCommandLine]);
|
||||
throw new CoreException(sprintf(self::ERROR_LABEL, $sError));
|
||||
}
|
||||
|
||||
$aClasses = json_decode($aOutput[0] ?? null, true);
|
||||
if (false === $aClasses) {
|
||||
$this->LogErrorWithProperLogger("Invalid JSON", null, ['env' => $sEnv, "output" => $sOutput]);
|
||||
throw new Exception("cannot get classes");
|
||||
$sMsg = sprintf(self::ERROR_LABEL, 'Invalid JSON');
|
||||
SetupLog::Error($sMsg, null, ['env' => $sEnv, "output" => $aOutput]);
|
||||
throw new CoreException($sMsg);
|
||||
}
|
||||
|
||||
if (!is_array($aClasses)) {
|
||||
$this->LogErrorWithProperLogger("not an array", null, ['env' => $sEnv, "classes" => $aClasses, "output" => $sOutput]);
|
||||
throw new Exception("cannot get classes from $sEnv");
|
||||
$sError = $aOutput[0] ?? 'Invalid json array when serializing model';
|
||||
SetupLog::Error(sprintf(self::ERROR_LABEL, '(JSON output not an array) '.$sError), null, ['env' => $sEnv, "classes" => $aClasses, "output" => $aOutput]);
|
||||
throw new CoreException(sprintf(self::ERROR_LABEL, $sError));
|
||||
}
|
||||
|
||||
return $aClasses;
|
||||
}
|
||||
|
||||
//could be shared with others in log APIs ?
|
||||
private function LogErrorWithProperLogger($sMessage, $sChannel = null, $aContext = []): void
|
||||
{
|
||||
if (ContextTag::Check(ContextTag::TAG_SETUP)) {
|
||||
SetupLog::Error($sMessage, $sChannel, $aContext);
|
||||
} else {
|
||||
IssueLog::Error($sMessage, $sChannel, $aContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,8 +21,7 @@ $sConfFile = utils::GetConfigFilePath($sEnv);
|
||||
try {
|
||||
MetaModel::Startup($sConfFile, false /* $bModelOnly */, false /* $bAllowCache */, false /* $bTraceSourceFiles */, $sEnv);
|
||||
} catch (\Throwable $e) {
|
||||
echo $e->getMessage();
|
||||
echo $e->getTraceAsString();
|
||||
SetupLog::Enable(APPROOT.'log/setup.log');
|
||||
\SetupLog::Error(
|
||||
"Cannot read model from provided environment",
|
||||
null,
|
||||
@@ -32,7 +31,9 @@ try {
|
||||
'stack' => $e->getTraceAsString(),
|
||||
]
|
||||
);
|
||||
echo "Cannot read model from provided environment";
|
||||
|
||||
//keep first echo to have proper setup feedbacks
|
||||
echo $e->getMessage();
|
||||
exit(1);
|
||||
}
|
||||
|
||||
|
||||
@@ -70,6 +70,7 @@ class DataAuditSequencer extends StepSequencer
|
||||
$aSelectedExtensionCodes = $this->oParams->Get('selected_extensions', []);
|
||||
$bUseSymbolicLinks = $this->oParams->Get('use_symbolic_links', null) === 'on';
|
||||
MetaModel::ResetAllCaches($this->oRunTimeEnvironment->GetBuildEnv());
|
||||
|
||||
$this->oRunTimeEnvironment->DoCompile(
|
||||
$aSelectedExtensionCodes,
|
||||
$aRemovedExtensionCodes,
|
||||
@@ -79,6 +80,7 @@ class DataAuditSequencer extends StepSequencer
|
||||
return $this->ComputeNextStep($sStep);
|
||||
|
||||
case 'setup-audit':
|
||||
$this->oRunTimeEnvironment->ExitMaintenanceMode();
|
||||
$this->oRunTimeEnvironment->DataToCleanupAudit();
|
||||
return $this->ComputeNextStep($sStep);
|
||||
|
||||
|
||||
@@ -103,6 +103,9 @@ class CheckResult
|
||||
*/
|
||||
class SetupUtils
|
||||
{
|
||||
// Name of the parameter array in session for setup
|
||||
public const SESSION_PARAMETERS_NAME = 'setup_params';
|
||||
|
||||
// -- Minimum versions (requirements : forbids installation if not met)
|
||||
public const PHP_MIN_VERSION = '8.2.0';
|
||||
public const MYSQL_MIN_VERSION = '5.7.0'; // 5.6 is no longer supported
|
||||
@@ -229,6 +232,13 @@ class SetupUtils
|
||||
$aResult[] = new CheckResult(CheckResult::WARNING, "Missing optional PHP extension: $sExtension. ".$sMessage);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
SetupUtils::CheckCliPhpVersionIsOk();
|
||||
} catch (CoreException $e) {
|
||||
$aResult[] = new CheckResult(CheckResult::WARNING, $e->getMessage());
|
||||
}
|
||||
|
||||
// Check some ini settings here
|
||||
if (function_exists('php_ini_loaded_file')) { // PHP >= 5.2.4
|
||||
$sPhpIniFile = php_ini_loaded_file();
|
||||
@@ -1427,7 +1437,7 @@ EOF
|
||||
// Unsupported Password, warn the user
|
||||
$oPage->add_ready_script(
|
||||
<<<JS
|
||||
$("#db_info").html('<div class="message message-error"><span class="message-title">Error:</span>On Windows, the backup won\'t work because database password contains %, ! or " character</div>');
|
||||
$("#db_info").html('<div class="message message-error ibo-is-html-content"><span class="message-title">Error:</span>On Windows, the backup won\'t work because database password contains %, ! or " character</div>');
|
||||
JS
|
||||
);
|
||||
} else {
|
||||
@@ -2189,6 +2199,41 @@ JS
|
||||
|
||||
return [$sButtonLabel, $sButtonUrl];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sErrorLabel: error label with 1 placeholder inside for detailed error
|
||||
* @return void
|
||||
* @throws \ConfigException
|
||||
* @throws \CoreException
|
||||
*/
|
||||
public static function CheckCliPhpVersionIsOk(string $sErrorLabel = '%s'): void
|
||||
{
|
||||
$sPHPExec = trim(utils::GetConfig()->Get('php_path'));
|
||||
$aOutput = null;
|
||||
$iRes = 0;
|
||||
exec("$sPHPExec --version", $aOutput, $iRes);
|
||||
if ($iRes != 0) {
|
||||
$sError = sprintf($sErrorLabel, "Cannot check CLI/PHP version ($sPHPExec)");
|
||||
SetupLog::Error($sError, null, ['code' => $iRes, "output" => $aOutput, 'php_path' => $sPHPExec]);
|
||||
throw new CoreException($sError);
|
||||
}
|
||||
|
||||
SetupUtils::CheckCliPhpVersionFromOutput($sErrorLabel, PHP_MAJOR_VERSION.'.'.PHP_MINOR_VERSION, $sPHPExec, $aOutput);
|
||||
}
|
||||
|
||||
private static function CheckCliPhpVersionFromOutput(string $sErrorLabel, string $sUIPhpVersion, string $sPHPExec, $aOutput): void
|
||||
{
|
||||
$sFoundVersion = trim($aOutput[0] ?? "");
|
||||
if (false !== preg_match('/(\d+\.\d+)(?:\.\d+)?/', $sFoundVersion, $aMatches)) {
|
||||
$sFoundVersion = $aMatches[1];
|
||||
}
|
||||
|
||||
if ($sFoundVersion !== $sUIPhpVersion) {
|
||||
$sError = sprintf($sErrorLabel, "Mismatch between PHP versions (CLI: $sFoundVersion/ UI: $sUIPhpVersion)");
|
||||
SetupLog::Error($sError, null, ["output" => $aOutput, 'php_path' => $sPHPExec]);
|
||||
throw new CoreException($sError);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -132,7 +132,7 @@ if ($bUseItopConfig && file_exists($sConfigFile)) {
|
||||
$aDBXmlSettings ['prefix'] = $oConfig->Get('db_subname');
|
||||
$aDBXmlSettings ['db_tls_enabled'] = $oConfig->Get('db_tls.enabled');
|
||||
//cannot be null or infinite loop triggered!
|
||||
$aDBXmlSettings ['db_tls_ca'] = $oConfig->Get('db_tls.ca') ?? "";
|
||||
$aDBXmlSettings ['db_tls_ca'] = $oConfig->Get('db_tls.ca') ?? '';
|
||||
$oParams->Set('database', $aDBXmlSettings);
|
||||
|
||||
$aFields = [
|
||||
|
||||
@@ -1,21 +1,11 @@
|
||||
<?php
|
||||
|
||||
// Copyright (C) 2010-2024 Combodo SAS
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// iTop is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// iTop is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
/**
|
||||
* @copyright Copyright (C) 2010-2026 Combodo SAS
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
use Combodo\iTop\Service\Session\SessionParameters;
|
||||
|
||||
require_once(APPROOT.'setup/setuputils.class.inc.php');
|
||||
require_once(APPROOT.'setup/parameters.class.inc.php');
|
||||
@@ -26,14 +16,12 @@ require_once(APPROOT.'setup/extensionsmap.class.inc.php');
|
||||
/**
|
||||
* Engine for displaying the various pages of a "wizard"
|
||||
* Each "step" of the wizard must be implemented as
|
||||
* separate class derived from WizardStep. each 'step' can also have its own
|
||||
* separate classes derived from WizardStep. Each 'step' can also have its own
|
||||
* internal 'state' for developing complex wizards.
|
||||
* The WizardController provides the "<< Back" feature by storing a stack
|
||||
* of the previous screens. The WizardController also maintains from page
|
||||
* to page a list of "parameters" to be dispayed/edited by each of the steps.
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2024 Combodo SAS
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
class WizardController
|
||||
@@ -41,37 +29,42 @@ class WizardController
|
||||
protected $aWizardSteps;
|
||||
protected $sInitialStepClass;
|
||||
protected $sInitialState;
|
||||
protected $aParameters;
|
||||
protected SessionParameters $oSessionParameters;
|
||||
|
||||
/**
|
||||
* Initiailization of the wizard controller
|
||||
* Initialization of the wizard controller
|
||||
* @param string $sInitialStepClass Class of the initial step/page of the wizard
|
||||
* @param string $sInitialState Initial state of the initial page (if this class manages states)
|
||||
*/
|
||||
public function __construct($sInitialStepClass, $sInitialState = '')
|
||||
public function __construct(string $sInitialStepClass, string $sInitialState = '')
|
||||
{
|
||||
$this->sInitialStepClass = $sInitialStepClass;
|
||||
$this->sInitialState = $sInitialState;
|
||||
$this->aParameters = [];
|
||||
$this->aWizardSteps = [];
|
||||
$this->oSessionParameters = new SessionParameters(SetupUtils::SESSION_PARAMETERS_NAME);
|
||||
$this->oSessionParameters->LogParameters();
|
||||
$this->aWizardSteps = $this->GetParameter('_steps', []);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pushes information about the current step onto the stack
|
||||
* @param array $aStepInfo Array('class' => , 'state' => )
|
||||
*/
|
||||
protected function PushStep($aStepInfo)
|
||||
protected function PushStep(array $aStepInfo): void
|
||||
{
|
||||
array_push($this->aWizardSteps, $aStepInfo);
|
||||
$this->aWizardSteps[] = $aStepInfo;
|
||||
$this->SetParameter('_steps', $this->aWizardSteps);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes information about the previous step from the stack
|
||||
* @return array{'class': string, 'state': string}
|
||||
*/
|
||||
protected function PopStep()
|
||||
protected function PopStep(): array
|
||||
{
|
||||
return array_pop($this->aWizardSteps);
|
||||
$aStep = array_pop($this->aWizardSteps);
|
||||
$this->SetParameter('_steps', $this->aWizardSteps);
|
||||
|
||||
return $aStep;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -79,13 +72,9 @@ class WizardController
|
||||
* @param string $sParamCode The code identifying this parameter
|
||||
* @param mixed $defaultValue The default value of the parameter in case it was not set
|
||||
*/
|
||||
public function GetParameter($sParamCode, $defaultValue = '')
|
||||
public function GetParameter(string $sParamCode, mixed $defaultValue = ''): mixed
|
||||
{
|
||||
if (array_key_exists($sParamCode, $this->aParameters)) {
|
||||
return $this->aParameters[$sParamCode];
|
||||
}
|
||||
|
||||
return $defaultValue;
|
||||
return $this->oSessionParameters->GetParameter($sParamCode, $defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -115,9 +104,9 @@ class WizardController
|
||||
* @param string $sParamCode The code identifying this parameter
|
||||
* @param mixed $value The value to store
|
||||
*/
|
||||
public function SetParameter($sParamCode, $value)
|
||||
public function SetParameter(string $sParamCode, mixed $value): void
|
||||
{
|
||||
$this->aParameters[$sParamCode] = $value;
|
||||
$this->oSessionParameters->SetParameter($sParamCode, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -126,30 +115,46 @@ class WizardController
|
||||
* @param mixed $defaultValue The default value for the parameter
|
||||
* @param string $sSanitizationFilter A 'sanitization' fitler. Default is 'raw_data', which means no filtering
|
||||
*/
|
||||
public function SaveParameter($sParamCode, $defaultValue, $sSanitizationFilter = 'raw_data')
|
||||
public function SaveParameter(string $sParamCode, mixed $defaultValue, string $sSanitizationFilter = 'raw_data'): void
|
||||
{
|
||||
$value = utils::ReadParam($sParamCode, $defaultValue, false, $sSanitizationFilter);
|
||||
$this->aParameters[$sParamCode] = $value;
|
||||
$this->oSessionParameters->SetParameterFromParams($sParamCode, $defaultValue, $sSanitizationFilter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores the value of the page's parameter in a "persistent" parameter in the wizard's context
|
||||
* @param string $sParamCode The code identifying this parameter
|
||||
* @param mixed $defaultValue The default value for the parameter
|
||||
* @param string $sSanitizationFilter A 'sanitization' fitler. Default is 'raw_data', which means no filtering
|
||||
*/
|
||||
public function SavePostedParameter(string $sParamCode, mixed $defaultValue = '', string $sSanitizationFilter = 'raw_data'): void
|
||||
{
|
||||
$this->oSessionParameters->SetParameterFromPostedParams($sParamCode, $defaultValue, $sSanitizationFilter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the wizard by displaying it in its initial state
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function Start()
|
||||
public function Start(): void
|
||||
{
|
||||
if ($this->GetParameter('return_application', '') === '') {
|
||||
// Fresh restart of the wizard
|
||||
$this->EraseParameters();
|
||||
}
|
||||
$sCurrentStepClass = $this->sInitialStepClass;
|
||||
$oStep = $this->GetWizardStep($sCurrentStepClass, $this->sInitialState);
|
||||
$oStep = $this->InstantiateWizardStep($sCurrentStepClass, $this->sInitialState);
|
||||
$this->DisplayStep($oStep);
|
||||
}
|
||||
/**
|
||||
* Progress towards the next step of the wizard
|
||||
* @throws Exception
|
||||
*/
|
||||
protected function Next()
|
||||
protected function Next(): void
|
||||
{
|
||||
$sCurrentStepClass = utils::ReadParam('_class', $this->sInitialStepClass);
|
||||
$sCurrentState = utils::ReadParam('_state', $this->sInitialState);
|
||||
$oStep = $this->GetWizardStep($sCurrentStepClass, $sCurrentState);
|
||||
$oStep = $this->InstantiateWizardStep($sCurrentStepClass, $sCurrentState);
|
||||
if ($oStep->ValidateParams()) {
|
||||
$aPossibleSteps = $oStep->GetPossibleSteps();
|
||||
if ($oStep->CanMoveBackward()) {
|
||||
@@ -157,7 +162,7 @@ class WizardController
|
||||
}
|
||||
$oWizardState = $oStep->UpdateWizardStateAndGetNextStep(true); // true => moving forward
|
||||
if (in_array($oWizardState->GetNextStep(), $aPossibleSteps)) {
|
||||
$oNextStep = $this->GetWizardStep($oWizardState->GetNextStep(), $oWizardState->GetState());
|
||||
$oNextStep = $this->InstantiateWizardStep($oWizardState->GetNextStep(), $oWizardState->GetState());
|
||||
$this->DisplayStep($oNextStep);
|
||||
} else {
|
||||
throw new Exception("Internal error: Unexpected next step '{$oWizardState->GetNextStep()}'. The possible next steps are: ".implode(', ', $aPossibleSteps));
|
||||
@@ -169,18 +174,20 @@ class WizardController
|
||||
|
||||
/**
|
||||
* Move one step back
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
protected function Back()
|
||||
protected function Back(): void
|
||||
{
|
||||
// let the current step save its parameters
|
||||
$sCurrentStepClass = utils::ReadParam('_class', $this->sInitialStepClass);
|
||||
$sCurrentState = utils::ReadParam('_state', $this->sInitialState);
|
||||
$oStep = $this->GetWizardStep($sCurrentStepClass, $sCurrentState);
|
||||
$oStep = $this->InstantiateWizardStep($sCurrentStepClass, $sCurrentState);
|
||||
$oStep->UpdateWizardStateAndGetNextStep(false); // false => Moving backwards
|
||||
|
||||
// Display the previous step
|
||||
$aCurrentStepInfo = $this->PopStep();
|
||||
$oStep = $this->GetWizardStep($aCurrentStepInfo['class'], $aCurrentStepInfo['state']);
|
||||
$oStep = $this->InstantiateWizardStep($aCurrentStepInfo['class'], $aCurrentStepInfo['state']);
|
||||
$this->DisplayStep($oStep);
|
||||
}
|
||||
|
||||
@@ -195,6 +202,24 @@ class WizardController
|
||||
{
|
||||
SetupLog::Info("=== Setup screen: ".$oStep->GetTitle().' ('.get_class($oStep).')');
|
||||
$oPage = new SetupPage($oStep->GetTitle());
|
||||
if (!$oStep->CanAccessToWizardStep()) {
|
||||
[$sButtonLabel, $sButtonUrl] = SetupUtils::GetBackButtonInfo($this->oSessionParameters->GetParameter('return_application', ''));
|
||||
SetupUtils::ExitReadOnlyMode(false); // Reset readonly mode in case of problem
|
||||
SetupUtils::EraseSetupToken();
|
||||
$this->oSessionParameters->Erase();
|
||||
$oP = new SetupPage('Installation Cannot Continue');
|
||||
$oP->add("<h2>Fatal error</h2>\n");
|
||||
$oP->error("<b>Error:</b> This setup step is not accessible with your access rights.");
|
||||
|
||||
$sButtonsHtml = <<<HTML
|
||||
<button type="button" class="ibo-button ibo-is-regular ibo-is-primary" onclick="window.location.href='$sButtonUrl'">$sButtonLabel</button>
|
||||
HTML;
|
||||
$oP->p($sButtonsHtml);
|
||||
|
||||
$oP->output();
|
||||
// Prevent token creation
|
||||
exit;
|
||||
}
|
||||
$oPage->LinkScriptFromAppRoot('setup/setup.js');
|
||||
|
||||
$oPage->add('<form id="wiz_form" class="ibo-setup--wizard" method="post">');
|
||||
@@ -208,11 +233,6 @@ class WizardController
|
||||
// to store the parameters
|
||||
$oPage->add('<input type="hidden" id="_class" name="_class" value="'.get_class($oStep).'"/>');
|
||||
$oPage->add('<input type="hidden" id="_state" name="_state" value="'.$oStep->GetState().'"/>');
|
||||
foreach ($this->aParameters as $sCode => $value) {
|
||||
$oPage->add('<input type="hidden" name="_params['.$sCode.']" value="'.utils::EscapeHtml($value).'"/>');
|
||||
}
|
||||
|
||||
$oPage->add('<input type="hidden" name="_steps" value="'.utils::EscapeHtml(json_encode($this->aWizardSteps)).'"/>');
|
||||
$oPage->add('<table style="width:100%;" class="ibo-setup--wizard--buttons-container"><tr>');
|
||||
if (count($this->aWizardSteps) > 0) {
|
||||
if ($oStep->CanMoveBackward()) {
|
||||
@@ -267,7 +287,7 @@ EOF
|
||||
* Make the wizard run: 'Start', 'Next' or 'Back' depending WizardUpdateButtons();
|
||||
* on the page's parameters
|
||||
*/
|
||||
public function Run()
|
||||
public function Run(): void
|
||||
{
|
||||
/**
|
||||
* @since 3.2.0 Add the ContextTag init
|
||||
@@ -276,8 +296,6 @@ EOF
|
||||
$oContextTag = new ContextTag(ContextTag::TAG_SETUP);
|
||||
|
||||
$sOperation = utils::ReadParam('operation');
|
||||
$this->aParameters = utils::ReadParam('_params', [], false, 'raw_data');
|
||||
$this->SetWizardSteps(json_decode(utils::ReadParam('_steps', '[]', false, 'raw_data'), true));
|
||||
|
||||
switch ($sOperation) {
|
||||
case 'next':
|
||||
@@ -293,65 +311,6 @@ EOF
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides information about the structure/workflow of the wizard by listing
|
||||
* the possible list of 'steps' and their dependencies
|
||||
* @param string $sStep Name of the class to start from (used for recursion)
|
||||
* @param array $aAllSteps List of steps (used for recursion)
|
||||
*/
|
||||
public function DumpStructure($sStep = '', $aAllSteps = null)
|
||||
{
|
||||
if ($aAllSteps == null) {
|
||||
$aAllSteps = [];
|
||||
}
|
||||
if ($sStep == '') {
|
||||
$sStep = $this->sInitialStepClass;
|
||||
}
|
||||
|
||||
$oStep = $this->GetWizardStep($sStep);
|
||||
$aAllSteps[$sStep] = $oStep->GetPossibleSteps();
|
||||
foreach ($aAllSteps[$sStep] as $sNextStep) {
|
||||
if (!array_key_exists($sNextStep, $aAllSteps)) {
|
||||
$aAllSteps = $this->DumpStructure($sNextStep, $aAllSteps);
|
||||
}
|
||||
}
|
||||
|
||||
return $aAllSteps;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dump the wizard's structure as a string suitable to produce a chart
|
||||
* using graphviz's "dot" program
|
||||
* @return string The 'dot' formatted output
|
||||
*/
|
||||
public function DumpStructureAsDot()
|
||||
{
|
||||
$aAllSteps = $this->DumpStructure();
|
||||
$sOutput = "digraph finite_state_machine {\n";
|
||||
//$sOutput .= "\trankdir=LR;";
|
||||
$sOutput .= "\tsize=\"10,12\"\n";
|
||||
|
||||
$aDeadEnds = [$this->sInitialStepClass];
|
||||
foreach ($aAllSteps as $sStep => $aNextSteps) {
|
||||
if (count($aNextSteps) == 0) {
|
||||
$aDeadEnds[] = $sStep;
|
||||
}
|
||||
}
|
||||
$sOutput .= "\tnode [shape = doublecircle]; ".implode(' ', $aDeadEnds).";\n";
|
||||
$sOutput .= "\tnode [shape = box];\n";
|
||||
foreach ($aAllSteps as $sStep => $aNextSteps) {
|
||||
$oStep = $this->GetWizardStep($sStep);
|
||||
$sOutput .= "\t$sStep [ label = \"".$oStep->GetTitle()."\"];\n";
|
||||
if (count($aNextSteps) > 0) {
|
||||
foreach ($aNextSteps as $sNextStep) {
|
||||
$sOutput .= "\t$sStep -> $sNextStep;\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
$sOutput .= "}\n";
|
||||
return $sOutput;
|
||||
}
|
||||
|
||||
public function SetWizardSteps(array $aWizardSteps): void
|
||||
{
|
||||
$this->aWizardSteps = $aWizardSteps;
|
||||
@@ -364,11 +323,16 @@ EOF
|
||||
* @return \WizardStep
|
||||
* @throws \Exception
|
||||
*/
|
||||
private function GetWizardStep(string $sCurrentStepClass, string $sCurrentState = ''): WizardStep
|
||||
private function InstantiateWizardStep(string $sCurrentStepClass, string $sCurrentState = ''): WizardStep
|
||||
{
|
||||
if (!is_subclass_of($sCurrentStepClass, WizardStep::class)) {
|
||||
throw new Exception('Unknown step '.$sCurrentStepClass);
|
||||
}
|
||||
return new $sCurrentStepClass($this, $sCurrentState);
|
||||
}
|
||||
|
||||
public function EraseParameters()
|
||||
{
|
||||
$this->oSessionParameters->Erase();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,9 +55,6 @@ abstract class AbstractWizStepInstall extends WizardStep
|
||||
|
||||
$sSourceDir = $this->oWizard->GetParameter('source_dir');
|
||||
if (($sMode == 'upgrade') && ($this->oWizard->GetParameter('upgrade_type') == 'keep-previous')) {
|
||||
//$sPreviousVersionDir = $this->oWizard->GetParameter('previous_version_dir');
|
||||
//$aCopies[] = ['source' => $sSourceDir, 'destination' => 'modules']; // Source is an absolute path, destination is relative to APPROOT
|
||||
//$aCopies[] = ['source' => $sPreviousVersionDir.'/portal', 'destination' => 'portal']; // Source is an absolute path, destination is relative to APPROOT
|
||||
$sSourceDir = APPROOT.'modules';
|
||||
}
|
||||
|
||||
|
||||
@@ -43,6 +43,11 @@ class WizStepDataAudit extends WizStepInstall
|
||||
return 'Next';
|
||||
}
|
||||
|
||||
public function CanAccessToWizardStep()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function CanMoveForward()
|
||||
{
|
||||
if ($this->CheckDependencies()) {
|
||||
@@ -135,7 +140,7 @@ HTML
|
||||
$sButtonUrl = utils::HtmlEntities($sButtonUrl);
|
||||
$oPage->add_ready_script(
|
||||
<<<JS
|
||||
$('.ibo-setup--wizard--buttons-container tr td:nth-child(1)').after('<td style="text-align:center;"><button id="return-button" class="ibo-button ibo-is-alternative ibo-is-neutral ibo-is-hidden" type="button" onclick="window.location.href=\'$sButtonUrl\'"><span class="ibo-button--label">$sButtonLabel</span></button></td>');
|
||||
$('.ibo-setup--wizard--buttons-container tr td:nth-child(1)').before('<td style="text-align:center;"><button id="return-button" class="ibo-button ibo-is-alternative ibo-is-neutral ibo-is-hidden" type="button" onclick="window.location.href=\'$sButtonUrl\'"><span class="ibo-button--label">$sButtonLabel</span></button></td>');
|
||||
JS
|
||||
);
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
*/
|
||||
|
||||
use Combodo\iTop\Application\WebPage\WebPage;
|
||||
|
||||
/**
|
||||
@@ -205,7 +206,7 @@ EOF
|
||||
$sUpgradeDMVersionToDisplay = utils::HtmlEntities($sUpgradeDMVersion);
|
||||
$oPage->add(
|
||||
<<<HTML
|
||||
<div class="message message-valid">The datamodel will be upgraded from version $sInstalledDataModelVersion to version $sUpgradeDMVersion.</div>
|
||||
<div class="message message-valid ibo-is-html-content">The datamodel will be upgraded from version $sInstalledDataModelVersion to version $sUpgradeDMVersion.</div>
|
||||
<input type="hidden" name="upgrade_type" value="use-compatible">
|
||||
<input type="hidden" name="datamodel_path" value="$sCompatibleDMDirToDisplay">
|
||||
<input type="hidden" name="datamodel_version" value="$sUpgradeDMVersionToDisplay">
|
||||
@@ -214,15 +215,59 @@ HTML
|
||||
|
||||
}
|
||||
|
||||
$oPage->add('<div id="db_info"></div>');
|
||||
|
||||
$sDBServer = json_encode($this->oWizard->GetParameter('db_server', ''));
|
||||
$sDBUser = json_encode($this->oWizard->GetParameter('db_user', ''));
|
||||
$sDBPwd = json_encode($this->oWizard->GetParameter('db_pwd', ''));
|
||||
$sDBName = json_encode($this->oWizard->GetParameter('db_name', ''));
|
||||
$sTlsCA = json_encode($this->oWizard->GetParameter('db_tls_ca', ''));
|
||||
$sTlsEnabled = $this->oWizard->GetParameter('db_tls_enabled', false) ? 1 : 0;
|
||||
|
||||
$oPage->add_ready_script(
|
||||
<<<EOF
|
||||
$("#changes_summary .title").on('click', function() { $(this).parent().toggleClass('closed'); } );
|
||||
$('input[name=upgrade_type]').on('click change', function() { WizardUpdateButtons(); });
|
||||
|
||||
var iCheckDBTimer = null;
|
||||
var oXHRCheckDB = null;
|
||||
|
||||
function CheckDBConnection()
|
||||
{
|
||||
// Don't call the server too often...
|
||||
if (iCheckDBTimer !== null)
|
||||
{
|
||||
clearTimeout(iCheckDBTimer);
|
||||
iCheckDBTimer = null;
|
||||
}
|
||||
iCheckDBTimer = setTimeout(DoCheckDBConnection, 500);
|
||||
}
|
||||
|
||||
function DoCheckDBConnection()
|
||||
{
|
||||
iCheckDBTimer = null;
|
||||
var oParams = {
|
||||
'db_server': $sDBServer,
|
||||
'db_user': $sDBUser,
|
||||
'db_pwd': $sDBPwd,
|
||||
'db_name': $sDBName,
|
||||
'db_tls_enabled': $sTlsEnabled,
|
||||
'db_tls_ca': $sTlsCA,
|
||||
}
|
||||
if ((oXHRCheckDB != null) && (oXHRCheckDB != undefined))
|
||||
{
|
||||
oXHRCheckDB.abort();
|
||||
oXHRCheckDB = null;
|
||||
}
|
||||
oXHRCheckDB = WizardAsyncAction("check_db", oParams);
|
||||
}
|
||||
|
||||
DoCheckDBConnection(); // Validate the initial values immediately
|
||||
EOF
|
||||
);
|
||||
|
||||
$oMutex = new iTopMutex(
|
||||
'cron'.$this->oWizard->GetParameter('db_name', '').$this->oWizard->GetParameter('db_prefix', ''),
|
||||
'cron'.$sDBName.$this->oWizard->GetParameter('db_prefix', ''),
|
||||
$this->oWizard->GetParameter('db_server', ''),
|
||||
$this->oWizard->GetParameter('db_user', ''),
|
||||
$this->oWizard->GetParameter('db_pwd', ''),
|
||||
@@ -235,6 +280,15 @@ EOF
|
||||
}
|
||||
}
|
||||
|
||||
public function AsyncAction(WebPage $oPage, $sCode, $aParameters)
|
||||
{
|
||||
switch ($sCode) {
|
||||
case 'check_db':
|
||||
SetupUtils::AsyncCheckDB($oPage, $aParameters);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public function CanMoveForward()
|
||||
{
|
||||
return $this->bCanMoveForward;
|
||||
|
||||
@@ -132,9 +132,15 @@ class WizStepDone extends WizardStep
|
||||
|
||||
if (false === $bHasBackup) {
|
||||
SetupUtils::EraseSetupToken();
|
||||
$this->oWizard->EraseParameters();
|
||||
}
|
||||
}
|
||||
|
||||
public function CanAccessToWizardStep()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function CanMoveForward()
|
||||
{
|
||||
return false;
|
||||
@@ -152,6 +158,7 @@ class WizStepDone extends WizardStep
|
||||
public function AsyncAction(WebPage $oPage, $sCode, $aParameters)
|
||||
{
|
||||
SetupUtils::EraseSetupToken();
|
||||
$this->oWizard->EraseParameters();
|
||||
// For security reasons: add the extension now so that this action can be used to read *only* .tar.gz files from the disk...
|
||||
$sBackupFile = $aParameters['backup'].'.tar.gz';
|
||||
if (file_exists($sBackupFile)) {
|
||||
|
||||
@@ -45,6 +45,11 @@ class WizStepInstall extends AbstractWizStepInstall
|
||||
return 'Continue';
|
||||
}
|
||||
|
||||
public function CanAccessToWizardStep()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function CanMoveForward()
|
||||
{
|
||||
if ($this->CheckDependencies()) {
|
||||
@@ -120,7 +125,7 @@ JS);
|
||||
$sButtonUrl = utils::HtmlEntities($sButtonUrl);
|
||||
$oPage->add_ready_script(
|
||||
<<<JS
|
||||
$('.ibo-setup--wizard--buttons-container tr td:nth-child(1)').after('<td style="text-align:center;"><button id="return-button" class="ibo-button ibo-is-alternative ibo-is-neutral ibo-is-hidden" type="button" onclick="window.location.href=\'$sButtonUrl\'"><span class="ibo-button--label">$sButtonLabel</span></button></td>');
|
||||
$('.ibo-setup--wizard--buttons-container tr td:nth-child(1)').before('<td style="text-align:center;"><button id="return-button" class="ibo-button ibo-is-alternative ibo-is-neutral ibo-is-hidden" type="button" onclick="window.location.href=\'$sButtonUrl\'"><span class="ibo-button--label">$sButtonLabel</span></button></td>');
|
||||
JS
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,216 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Copyright (C) 2013-2026 Combodo SAS
|
||||
*
|
||||
* This file is part of iTop.
|
||||
*
|
||||
* iTop is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* iTop is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
*/
|
||||
|
||||
use Combodo\iTop\Application\WebPage\WebPage;
|
||||
|
||||
/**
|
||||
* 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::class, WizStepLicense::class];
|
||||
}
|
||||
|
||||
public function UpdateWizardStateAndGetNextStep($bMoveForward = true): WizardState
|
||||
{
|
||||
$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_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::class;
|
||||
} else {
|
||||
$this->oWizard->SetParameter('install_mode', 'upgrade');
|
||||
$sNextStep = WizStepDetectedInfo::class;
|
||||
|
||||
}
|
||||
return new WizardState($sNextStep);
|
||||
}
|
||||
|
||||
public function Display(SetupPage $oPage): void
|
||||
{
|
||||
$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', '');
|
||||
$sTlsEnabled = $this->oWizard->GetParameter('db_tls_enabled', false);
|
||||
$sTlsCA = $this->oWizard->GetParameter('db_tls_ca', '');
|
||||
$sPreviousVersionDir = '';
|
||||
if ($sInstallMode == '') {
|
||||
$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']);
|
||||
$sPreviousVersionDir = APPROOT;
|
||||
} else {
|
||||
$sInstallMode = 'install';
|
||||
}
|
||||
}
|
||||
$sPreviousVersionDir = $this->oWizard->GetParameter('previous_version_dir', $sPreviousVersionDir);
|
||||
|
||||
$sUpgradeInfoStyle = '';
|
||||
if ($sInstallMode == 'install') {
|
||||
$sUpgradeInfoStyle = ' style="display: none;" ';
|
||||
}
|
||||
$oPage->add('<div class="setup-content-title">What do you want to do?</div>');
|
||||
$sChecked = ($sInstallMode == 'install') ? ' checked ' : '';
|
||||
$oPage->p('<input id="radio_install" type="radio" name="install_mode" value="install" '.$sChecked.'/><label for="radio_install"> Install a new '.ITOP_APPLICATION.'</label>');
|
||||
$sChecked = ($sInstallMode == 'upgrade') ? ' checked ' : '';
|
||||
$sDisabled = (($sInstallMode == 'install') && (empty($sPreviousVersionDir))) ? ' disabled' : '';
|
||||
$oPage->p('<input id="radio_update" type="radio" name="install_mode" value="upgrade" '.$sChecked.$sDisabled.'/><label for="radio_update"> Upgrade an existing '.ITOP_APPLICATION.' instance</label>');
|
||||
|
||||
$sUpgradeDir = utils::HtmlEntities($sPreviousVersionDir);
|
||||
$oPage->add(
|
||||
<<<HTML
|
||||
<div id="upgrade_info"'.$sUpgradeInfoStyle.'>
|
||||
<div class="setup-disk-location--input--container">Location on the disk:<input id="previous_version_dir_display" type="text" value="$sUpgradeDir" class="ibo-input" disabled>
|
||||
<input type="hidden" name="previous_version_dir" value="$sUpgradeDir"></div>
|
||||
HTML
|
||||
);
|
||||
|
||||
SetupUtils::DisplayDBParameters(
|
||||
$oPage,
|
||||
false,
|
||||
$sDBServer,
|
||||
$sDBUser,
|
||||
$sDBPwd,
|
||||
$sDBName,
|
||||
$sDBPrefix,
|
||||
$sTlsEnabled,
|
||||
$sTlsCA
|
||||
);
|
||||
|
||||
$oPage->add('</div>');
|
||||
//$oPage->add('</fieldset>');
|
||||
$oPage->add_ready_script(
|
||||
<<<JS
|
||||
$("#radio_update").on('change', function() { if (this.checked ) { $('#upgrade_info').show(); WizardUpdateButtons(); } else { $('#upgrade_info').hide(); } });
|
||||
$("#radio_install").on('change', function() { if (this.checked ) { $('#upgrade_info').hide(); WizardUpdateButtons(); } else { $('#upgrade_info').show(); } });
|
||||
JS
|
||||
);
|
||||
}
|
||||
|
||||
public function AsyncAction(WebPage $oPage, $sCode, $aParameters)
|
||||
{
|
||||
switch ($sCode) {
|
||||
case 'check_path':
|
||||
$sPreviousVersionDir = $aParameters['previous_version_dir'];
|
||||
$aPreviousInstance = SetupUtils::GetPreviousInstance($sPreviousVersionDir);
|
||||
if ($aPreviousInstance['found']) {
|
||||
$sDBServer = utils::EscapeHtml($aPreviousInstance['db_server']);
|
||||
$sDBUser = utils::EscapeHtml($aPreviousInstance['db_user']);
|
||||
$sDBPwd = utils::EscapeHtml($aPreviousInstance['db_pwd']);
|
||||
$sDBName = utils::EscapeHtml($aPreviousInstance['db_name']);
|
||||
$sDBPrefix = utils::EscapeHtml($aPreviousInstance['db_prefix']);
|
||||
$oPage->add_ready_script(
|
||||
<<<EOF
|
||||
$("#db_server").val('$sDBServer');
|
||||
$("#db_user").val('$sDBUser');
|
||||
$("#db_pwd").val('$sDBPwd');
|
||||
$("#db_name").val('$sDBName');
|
||||
$("#db_prefix").val('$sDBPrefix');
|
||||
$("#db_pwd").trigger('change'); // Forces check of the DB connection
|
||||
EOF
|
||||
);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'check_db':
|
||||
SetupUtils::AsyncCheckDB($oPage, $aParameters);
|
||||
break;
|
||||
|
||||
case 'check_backup':
|
||||
$sDBBackupPath = $aParameters['db_backup_path'];
|
||||
$fFreeSpace = SetupUtils::CheckDiskSpace($sDBBackupPath);
|
||||
if ($fFreeSpace !== false) {
|
||||
$sMessage = utils::EscapeHtml(SetupUtils::HumanReadableSize($fFreeSpace).' free in '.dirname($sDBBackupPath));
|
||||
$oPage->add_ready_script(
|
||||
<<<EOF
|
||||
$("#backup_info").html('$sMessage');
|
||||
EOF
|
||||
);
|
||||
} else {
|
||||
$oPage->add_ready_script(
|
||||
<<<EOF
|
||||
$("#backup_info").html('');
|
||||
EOF
|
||||
);
|
||||
}
|
||||
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
|
||||
<<<EOF
|
||||
if ($("#radio_install").prop("checked"))
|
||||
{
|
||||
ValidateField("db_name", false);
|
||||
ValidateField("db_new_name", false);
|
||||
ValidateField("db_prefix", false);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
var bRet = ($("#wiz_form").data("db_connection") !== "error");
|
||||
bRet = ValidateField("db_name", true) && bRet;
|
||||
bRet = ValidateField("db_new_name", true) && bRet;
|
||||
bRet = ValidateField("db_prefix", true) && bRet;
|
||||
|
||||
return bRet;
|
||||
}
|
||||
EOF
|
||||
;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -34,7 +34,8 @@ class WizStepLandingBeforeAudit extends WizStepModulesChoice
|
||||
$oWizard->SetParameter('extensions_not_uninstallable', '[]');
|
||||
|
||||
$oWizard->SaveParameter('use_symbolic_links', MFCompiler::UseSymbolicLinks());
|
||||
$oWizard->SaveParameter('force-uninstall', '');
|
||||
$oWizard->SaveParameter('force-uninstall', false);
|
||||
$oWizard->SaveParameter('skip_wizard', false);
|
||||
|
||||
// should be done at the end
|
||||
parent::__construct($oWizard, $sCurrentState, false);
|
||||
@@ -52,7 +53,8 @@ class WizStepLandingBeforeAudit extends WizStepModulesChoice
|
||||
*/
|
||||
public function UpdateWizardStateAndGetNextStep($bMoveForward = true): WizardState
|
||||
{
|
||||
if ($this->oWizard->GetParameter('skip_wizard', false)) {
|
||||
$bSkipWizard = $this->oWizard->GetParameter('skip_wizard', false);
|
||||
if ($bSkipWizard) {
|
||||
$oRuntimeEnv = new RunTimeEnvironment();
|
||||
$sBuildConfigFile = APPCONF.$oRuntimeEnv->GetBuildEnv().'/'.ITOP_CONFIG_FILE;
|
||||
$oConfig = new Config($sBuildConfigFile);
|
||||
@@ -61,24 +63,45 @@ class WizStepLandingBeforeAudit extends WizStepModulesChoice
|
||||
$this->oWizard->SetParameter('selected_extensions', json_encode($aExtensionsFromDatabase));
|
||||
$adModulesFromDatabase = ModuleInstallationRepository::GetInstance()->ReadComputeInstalledModules($oConfig);
|
||||
$this->oWizard->SetParameter('selected_modules', json_encode(array_keys($adModulesFromDatabase)));
|
||||
} else {
|
||||
$this->oWizard->SavePostedParameter('selected_modules');
|
||||
$this->oWizard->SavePostedParameter('selected_extensions');
|
||||
$this->oWizard->SavePostedParameter('added_extensions');
|
||||
$this->oWizard->SavePostedParameter('removed_extensions');
|
||||
$this->oWizard->SavePostedParameter('extensions_not_uninstallable');
|
||||
$this->oWizard->SavePostedParameter('copy_setup_files', '1');
|
||||
$this->oWizard->SavePostedParameter('force-uninstall');
|
||||
$this->oWizard->SavePostedParameter('use_symbolic_links');
|
||||
$this->oWizard->SavePostedParameter('return_application');
|
||||
$this->oWizard->SavePostedParameter('target_env');
|
||||
}
|
||||
|
||||
$aWizardSteps = $this->GetWizardSteps();
|
||||
$this->oWizard->SetWizardSteps($aWizardSteps);
|
||||
$this->sCurrentState = count($aWizardSteps) - 1;
|
||||
$aWizardSteps = $this->oWizard->GetParameter('_steps', null);
|
||||
if (is_null($aWizardSteps)) {
|
||||
$aWizardSteps = $this->GetWizardSteps();
|
||||
if ($bSkipWizard) {
|
||||
$this->oWizard->SetParameter('_steps', $aWizardSteps);
|
||||
}
|
||||
|
||||
$aSelectedComponents = $this->GetSelectedComponents($this->aSteps, $this->oWizard->GetParameter('selected_extensions'));
|
||||
$this->oWizard->SetParameter('selected_components', json_encode($aSelectedComponents));
|
||||
// Component selection in previous screens
|
||||
if ($this->oWizard->GetParameter('selected_components', '[]') === '[]') {
|
||||
|
||||
// Save the choices for the summary step
|
||||
$sDisplayChoices = '<ul>';
|
||||
$i = 0;
|
||||
foreach ($this->aSteps as $aStepInfo) {
|
||||
$sDisplayChoices .= $this->GetSelectedModules($aStepInfo, $aSelectedComponents[$i], $aModules, '', '', $aExtensions);
|
||||
$i++;
|
||||
$aSelectedComponents = $this->GetSelectedComponents($this->aSteps, $this->oWizard->GetParameter('selected_extensions', '[]'));
|
||||
$this->oWizard->SetParameter('selected_components', json_encode($aSelectedComponents));
|
||||
} else {
|
||||
$aSelectedComponents = json_decode($this->oWizard->GetParameter('selected_components'), true);
|
||||
}
|
||||
|
||||
// Save the choices for the summary step
|
||||
$sDisplayChoices = '<ul>';
|
||||
$i = 0;
|
||||
foreach ($this->aSteps as $aStepInfo) {
|
||||
$sDisplayChoices .= $this->GetSelectedModules($aStepInfo, $aSelectedComponents[$i], $aModules, '', '', $aExtensions);
|
||||
$i++;
|
||||
}
|
||||
$sDisplayChoices .= '</ul>';
|
||||
$this->oWizard->SetParameter('display_choices', $sDisplayChoices);
|
||||
}
|
||||
$sDisplayChoices .= '</ul>';
|
||||
$this->oWizard->SetParameter('display_choices', $sDisplayChoices);
|
||||
|
||||
return new WizardState(WizStepDataAudit::class);
|
||||
}
|
||||
|
||||
@@ -171,13 +171,13 @@ class WizStepModulesChoice extends AbstractWizStepInstall
|
||||
{
|
||||
$aSteps = [
|
||||
["class" => "WizStepWelcome","state" => ""],
|
||||
["class" => "WizStepInstallOrUpgrade","state" => ""],
|
||||
["class" => "WizStepDetectedInfo","state" => ""],
|
||||
["class" => "WizStepUpgradeMiscParams","state" => ""],
|
||||
];
|
||||
$i = 0;
|
||||
$this->aSteps = null;
|
||||
while (null != $this->GetStepInfo($i)) {
|
||||
// Allow looping for all existing steps
|
||||
$this->aSteps = null;
|
||||
$aSteps [] = ["class" => "WizStepModulesChoice","state" => "$i"];
|
||||
$i++;
|
||||
|
||||
@@ -39,6 +39,11 @@ class WizStepSummary extends AbstractWizStepInstall
|
||||
return [WizStepInstall::class];
|
||||
}
|
||||
|
||||
public function CanAccessToWizardStep()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the label for the " Next >> " button
|
||||
* @return string The label for the button
|
||||
@@ -123,21 +128,23 @@ class WizStepSummary extends AbstractWizStepInstall
|
||||
$oPage->add($sExtensionsRemoved);
|
||||
$oPage->add('</div>');
|
||||
|
||||
$oPage->add('<div class="closed"><a class="title ibo-setup-summary-title" href="#" aria-label="Database Parameters">Database Parameters</a><ul>');
|
||||
$oPage->add('<li>Server Name: '.$aInstallParams['database']['server'].'</li>');
|
||||
$oPage->add('<li>DB User Name: '.$aInstallParams['database']['user'].'</li>');
|
||||
$oPage->add('<li>DB user password: ***</li>');
|
||||
if (($sMode == 'install') && ($this->oWizard->GetParameter('create_db') == 'yes')) {
|
||||
$oPage->add('<li>Database Name: '.$aInstallParams['database']['name'].' (will be created)</li>');
|
||||
} else {
|
||||
$oPage->add('<li>Database Name: '.$aInstallParams['database']['name'].'</li>');
|
||||
if ($this->oWizard->GetParameter('return_application') === '') {
|
||||
$oPage->add('<div class="closed"><a class="title ibo-setup-summary-title" href="#" aria-label="Database Parameters">Database Parameters</a><ul>');
|
||||
$oPage->add('<li>Server Name: '.$aInstallParams['database']['server'].'</li>');
|
||||
$oPage->add('<li>DB User Name: '.$aInstallParams['database']['user'].'</li>');
|
||||
$oPage->add('<li>DB user password: ***</li>');
|
||||
if (($sMode == 'install') && ($this->oWizard->GetParameter('create_db') == 'yes')) {
|
||||
$oPage->add('<li>Database Name: '.$aInstallParams['database']['name'].' (will be created)</li>');
|
||||
} else {
|
||||
$oPage->add('<li>Database Name: '.$aInstallParams['database']['name'].'</li>');
|
||||
}
|
||||
if ($aInstallParams['database']['prefix'] != '') {
|
||||
$oPage->add('<li>Prefix for the '.ITOP_APPLICATION.' tables: '.$aInstallParams['database']['prefix'].'</li>');
|
||||
} else {
|
||||
$oPage->add('<li>Prefix for the '.ITOP_APPLICATION.' tables: none</li>');
|
||||
}
|
||||
$oPage->add('</ul></div>');
|
||||
}
|
||||
if ($aInstallParams['database']['prefix'] != '') {
|
||||
$oPage->add('<li>Prefix for the '.ITOP_APPLICATION.' tables: '.$aInstallParams['database']['prefix'].'</li>');
|
||||
} else {
|
||||
$oPage->add('<li>Prefix for the '.ITOP_APPLICATION.' tables: none</li>');
|
||||
}
|
||||
$oPage->add('</ul></div>');
|
||||
|
||||
$oPage->add('<div class="closed"><a class="title ibo-setup-summary-title" href="#" aria-label="Data Model Configuration">Data Model Configuration</a>');
|
||||
$oPage->add($this->oWizard->GetParameter('display_choices'));
|
||||
@@ -253,7 +260,7 @@ JS
|
||||
$sButtonUrl = utils::HtmlEntities($sButtonUrl);
|
||||
$oPage->add_ready_script(
|
||||
<<<JS
|
||||
$('.ibo-setup--wizard--buttons-container tr td:nth-child(1)').after('<td style="text-align:center;"><button id="return-button" class="ibo-button ibo-is-alternative ibo-is-neutral" type="button" onclick="window.location.href=\'$sButtonUrl\'"><span class="ibo-button--label">$sButtonLabel</span></button></td>');
|
||||
$('.ibo-setup--wizard--buttons-container tr td:nth-child(1)').before('<td style="text-align:center;"><button id="return-button" class="ibo-button ibo-is-alternative ibo-is-neutral" type="button" onclick="window.location.href=\'$sButtonUrl\'"><span class="ibo-button--label">$sButtonLabel</span></button></td>');
|
||||
JS
|
||||
);
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ class WizStepWelcome extends WizardStep
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the label for the " Next >> " button
|
||||
* Returns the label for the "Next >>" button
|
||||
* @return string The label for the button
|
||||
*/
|
||||
public function GetNextButtonLabel()
|
||||
@@ -50,16 +50,43 @@ class WizStepWelcome extends WizardStep
|
||||
|
||||
public function GetPossibleSteps()
|
||||
{
|
||||
return [WizStepInstallOrUpgrade::class];
|
||||
return [WizStepDetectedInfo::class, WizStepLicense::class];
|
||||
}
|
||||
|
||||
public function UpdateWizardStateAndGetNextStep($bMoveForward = true): WizardState
|
||||
{
|
||||
return new WizardState(WizStepInstallOrUpgrade::class);
|
||||
if ($this->oWizard->GetParameter('install_mode', 'install') === 'install') {
|
||||
|
||||
return new WizardState(WizStepLicense::class);
|
||||
}
|
||||
|
||||
return new WizardState(WizStepDetectedInfo::class);
|
||||
}
|
||||
|
||||
public function Display(SetupPage $oPage): void
|
||||
{
|
||||
$this->oWizard->EraseParameters();
|
||||
$this->oWizard->SetWizardSteps([]);
|
||||
|
||||
$aPreviousInstance = SetupUtils::GetPreviousInstance(APPROOT);
|
||||
if ($aPreviousInstance['found']) {
|
||||
$this->oWizard->SetParameter('install_mode', 'upgrade');
|
||||
$this->oWizard->SetParameter('db_server', $aPreviousInstance['db_server']);
|
||||
$this->oWizard->SetParameter('db_user', $aPreviousInstance['db_user']);
|
||||
$this->oWizard->SetParameter('db_pwd', $aPreviousInstance['db_pwd']);
|
||||
$this->oWizard->SetParameter('db_name', $aPreviousInstance['db_name']);
|
||||
$this->oWizard->SetParameter('db_prefix', $aPreviousInstance['db_prefix']);
|
||||
$this->oWizard->SetParameter('db_tls_enabled', $aPreviousInstance['db_tls_enabled']);
|
||||
$this->oWizard->SetParameter('db_tls_ca', $aPreviousInstance['db_tls_ca'] ?? '');
|
||||
$this->oWizard->SetParameter('graphviz_path', $aPreviousInstance['graphviz_path']);
|
||||
} else {
|
||||
$this->oWizard->SetParameter('install_mode', 'install');
|
||||
$sFullSourceDir = SetupUtils::GetLatestDataModelDir();
|
||||
$this->oWizard->SetParameter('source_dir', $sFullSourceDir);
|
||||
$this->oWizard->SetParameter('datamodel_version', SetupUtils::GetDataModelVersion($sFullSourceDir));
|
||||
}
|
||||
$this->oWizard->SetParameter('previous_version_dir', APPROOT);
|
||||
|
||||
// Store the misc_options for the future...
|
||||
$aMiscOptions = utils::ReadParam('option', [], false, 'raw_data');
|
||||
$sMiscOptions = $this->oWizard->GetParameter('misc_options', json_encode($aMiscOptions));
|
||||
@@ -127,7 +154,7 @@ HTML
|
||||
<form id="fast_setup" method="post">
|
||||
<input type="hidden" name="_class" value="WizStepLandingBeforeAudit"/>
|
||||
<input type="hidden" name="operation" value="next"/>
|
||||
<input type="hidden" name="_params[skip_wizard]" value="1"/>
|
||||
<input type="hidden" name="skip_wizard" value="1"/>
|
||||
</form>
|
||||
HTML
|
||||
);
|
||||
|
||||
@@ -24,7 +24,6 @@
|
||||
* Steps order (can be retrieved using \WizardController::DumpStructure) :
|
||||
*
|
||||
* WizStepWelcome
|
||||
* WizStepInstallOrUpgrade
|
||||
* + +
|
||||
* | |
|
||||
* v +----->
|
||||
@@ -76,6 +75,11 @@ abstract class WizardStep
|
||||
{
|
||||
}
|
||||
|
||||
public function CanAccessToWizardStep()
|
||||
{
|
||||
return ($this->oWizard->GetParameter('return_application', '') === '');
|
||||
}
|
||||
|
||||
protected function CheckDependencies()
|
||||
{
|
||||
if (is_null($this->bDependencyCheck)) {
|
||||
@@ -187,15 +191,6 @@ abstract class WizardStep
|
||||
return 'return true;';
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells whether this step of the wizard requires that the configuration file be writable
|
||||
* @return bool True if the wizard will possibly need to modify the configuration at some point
|
||||
*/
|
||||
public function RequiresWritableConfig()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Overload this function to implement asynchronous action(s) (AJAX)
|
||||
* @param string $sCode The code of the action (if several actions need to be distinguished)
|
||||
|
||||
@@ -4,7 +4,6 @@ require_once(APPROOT.'setup/wizardsteps/WizardState.php');
|
||||
require_once(APPROOT.'setup/wizardsteps/WizardStep.php');
|
||||
require_once(APPROOT.'setup/wizardsteps/AbstractWizStepInstall.php');
|
||||
require_once(APPROOT.'setup/wizardsteps/WizStepWelcome.php');
|
||||
require_once(APPROOT.'setup/wizardsteps/WizStepInstallOrUpgrade.php');
|
||||
require_once(APPROOT.'setup/wizardsteps/WizStepDetectedInfo.php');
|
||||
require_once(APPROOT.'setup/wizardsteps/WizStepLicense.php');
|
||||
require_once(APPROOT.'setup/wizardsteps/WizStepLicense2.php');
|
||||
|
||||
@@ -166,8 +166,10 @@ class AttributeImage extends AttributeBlob
|
||||
return null;
|
||||
}
|
||||
|
||||
$bExistingImageModified = ($oHostObject->IsModified() && (array_key_exists($this->GetCode(), $oHostObject->ListChanges())));
|
||||
if ($oHostObject->IsNew() || ($bExistingImageModified)) {
|
||||
if (is_null($oHostObject)
|
||||
|| $oHostObject->IsNew()
|
||||
|| ($oHostObject->IsModified() && array_key_exists($this->GetCode(), $oHostObject->ListChanges())) //$bExistingImageModified
|
||||
) {
|
||||
// If the object is modified (or not yet stored in the database) we must serve the content of the image directly inline
|
||||
// otherwise (if we just give an URL) the browser will be given the wrong content... and may cache it
|
||||
return 'data:'.$value->GetMimeType().';base64,'.base64_encode($value->GetData());
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @copyright Copyright (C) 2010-2024 Combodo SAS
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2026 Combodo SAS
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\Application\Helper;
|
||||
namespace Combodo\iTop\Service\Session;
|
||||
|
||||
use Combodo\iTop\SessionTracker\SessionHandler;
|
||||
|
||||
@@ -21,9 +21,9 @@ class Session
|
||||
/** @var int|null */
|
||||
public static $iSessionId = null;
|
||||
/** @var bool */
|
||||
public static $bAllowCLI = false;
|
||||
public static bool $bAllowCLI = false;
|
||||
|
||||
public static function Start()
|
||||
public static function Start(): void
|
||||
{
|
||||
if (session_status() === PHP_SESSION_DISABLED) {
|
||||
return;
|
||||
@@ -47,7 +47,7 @@ class Session
|
||||
self::$iSessionId = session_id();
|
||||
}
|
||||
|
||||
public static function RegenerateId($bDeleteOldSession = false)
|
||||
public static function RegenerateId($bDeleteOldSession = false): void
|
||||
{
|
||||
if (session_status() === PHP_SESSION_DISABLED || headers_sent()) {
|
||||
return;
|
||||
@@ -61,7 +61,7 @@ class Session
|
||||
self::$iSessionId = session_id();
|
||||
}
|
||||
|
||||
public static function WriteClose()
|
||||
public static function WriteClose(): void
|
||||
{
|
||||
if (session_status() === PHP_SESSION_DISABLED) {
|
||||
return;
|
||||
@@ -76,7 +76,7 @@ class Session
|
||||
* @param string|array $key key to access to the session variable. To access to $_SESSION['a']['b'] $key must be ['a', 'b']
|
||||
* @param $value
|
||||
*/
|
||||
public static function Set($key, $value)
|
||||
public static function Set($key, $value): void
|
||||
{
|
||||
if (!isset($_SESSION) || self::Get($key) == $value) {
|
||||
return;
|
||||
@@ -103,7 +103,7 @@ class Session
|
||||
/**
|
||||
* @param string|array $key key to access to the session variable. To access to $_SESSION['a']['b'] $key must be ['a', 'b']
|
||||
*/
|
||||
public static function Unset($key)
|
||||
public static function Unset($key): void
|
||||
{
|
||||
if (self::IsSet($key)) {
|
||||
$aSession = $_SESSION;
|
||||
@@ -137,7 +137,7 @@ class Session
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public static function Get($key, $default = null)
|
||||
public static function Get($key, $default = null): mixed
|
||||
{
|
||||
if (isset($_SESSION)) {
|
||||
$aSession = $_SESSION;
|
||||
@@ -189,7 +189,7 @@ class Session
|
||||
/**
|
||||
* @return bool|string
|
||||
*/
|
||||
public static function GetLog()
|
||||
public static function GetLog(): string
|
||||
{
|
||||
return print_r($_SESSION, true);
|
||||
}
|
||||
96
sources/Service/Session/SessionParameters.php
Normal file
96
sources/Service/Session/SessionParameters.php
Normal file
@@ -0,0 +1,96 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2026 Combodo SAS
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\Service\Session;
|
||||
|
||||
use IssueLog;
|
||||
use LogChannels;
|
||||
use utils;
|
||||
|
||||
class SessionParameters
|
||||
{
|
||||
private string $sSessionArrayName;
|
||||
private array $aParameters;
|
||||
|
||||
public function __construct(string $sParamsName)
|
||||
{
|
||||
$this->sSessionArrayName = $sParamsName;
|
||||
$this->aParameters = Session::Get($this->sSessionArrayName, []);
|
||||
IssueLog::Enable(APPROOT.'log/error.log');
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a "persistent" parameter from the wizard's context
|
||||
* @param string $sParamCode The code identifying this parameter
|
||||
* @param mixed $defaultValue The default value of the parameter in case it was not set
|
||||
*/
|
||||
public function GetParameter(string $sParamCode, mixed $defaultValue = '')
|
||||
{
|
||||
if (array_key_exists($sParamCode, $this->aParameters)) {
|
||||
return $this->aParameters[$sParamCode];
|
||||
}
|
||||
|
||||
return $defaultValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores a "persistent" parameter in the wizard's context
|
||||
*
|
||||
* @param string $sParamCode The code identifying this parameter
|
||||
* @param mixed $value The value to store
|
||||
*/
|
||||
public function SetParameter($sParamCode, $value): void
|
||||
{
|
||||
$this->aParameters[$sParamCode] = $value;
|
||||
$this->Save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores the value of the page's parameter in a "persistent" parameter in the wizard's context
|
||||
* @param string $sParamCode The code identifying this parameter
|
||||
* @param mixed $defaultValue The default value for the parameter
|
||||
* @param string $sSanitizationFilter A 'sanitization' fitler. Default is 'raw_data', which means no filtering
|
||||
*/
|
||||
public function SetParameterFromPostedParams(string $sParamCode, mixed $defaultValue, string $sSanitizationFilter = 'raw_data'): void
|
||||
{
|
||||
$value = utils::ReadPostedParam($sParamCode, $defaultValue, $sSanitizationFilter);
|
||||
$this->aParameters[$sParamCode] = $value;
|
||||
$this->Save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores the value of the page's parameter in a "persistent" parameter in the wizard's context
|
||||
* @param string $sParamCode The code identifying this parameter
|
||||
* @param mixed $defaultValue The default value for the parameter
|
||||
* @param string $sSanitizationFilter A 'sanitization' fitler. Default is 'raw_data', which means no filtering
|
||||
*/
|
||||
public function SetParameterFromParams(string $sParamCode, mixed $defaultValue, string $sSanitizationFilter = 'raw_data'): void
|
||||
{
|
||||
$value = utils::ReadParam($sParamCode, $defaultValue, false, $sSanitizationFilter);
|
||||
$this->aParameters[$sParamCode] = $value;
|
||||
$this->Save();
|
||||
}
|
||||
|
||||
private function Save(): void
|
||||
{
|
||||
Session::Set($this->sSessionArrayName, $this->aParameters);
|
||||
}
|
||||
|
||||
public function Erase(): void
|
||||
{
|
||||
$this->aParameters = [];
|
||||
$this->Save();
|
||||
$this->LogParameters();
|
||||
}
|
||||
|
||||
public function LogParameters(): void
|
||||
{
|
||||
IssueLog::Debug('---------------------------------', LogChannels::SESSION_PARAMETERS);
|
||||
IssueLog::Debug(json_encode(Session::Get($this->sSessionArrayName, []), JSON_PRETTY_PRINT), LogChannels::SESSION_PARAMETERS);
|
||||
IssueLog::Debug('---------------------------------', LogChannels::SESSION_PARAMETERS);
|
||||
}
|
||||
}
|
||||
@@ -121,3 +121,6 @@ class_alias(\Combodo\iTop\PropertyType\ValueType\Leaf\ValueTypeText::class, 'Com
|
||||
class_alias(\Combodo\iTop\PropertyType\Serializer\XMLFormat\XMLFormatCSV::class, 'Combodo-XMLFormat-CSV');
|
||||
class_alias(\Combodo\iTop\PropertyType\Serializer\XMLFormat\XMLFormatValueAsId::class, 'Combodo-XMLFormat-ValueAsId');
|
||||
class_alias(\Combodo\iTop\PropertyType\Serializer\XMLFormat\XMLFormatFlatArray::class, 'Combodo-XMLFormat-FlatArray');
|
||||
|
||||
// Moved classes
|
||||
class_alias(\Combodo\iTop\Service\Session\Session::class, '\Combodo\iTop\Application\Helper\Session');
|
||||
|
||||
@@ -3002,6 +3002,8 @@ class SynchroExecution
|
||||
* </ul>
|
||||
*/
|
||||
protected $m_oLastFullLoadStartDate = null;
|
||||
/** @var bool true if the caller script gave the datetime before import phase was launched */
|
||||
protected $m_bIsImportPhaseDateKnown;
|
||||
|
||||
/** @var \CMDBChange */
|
||||
protected $m_oChange = null;
|
||||
@@ -3026,7 +3028,10 @@ class SynchroExecution
|
||||
public function __construct($oDataSource, $oImportPhaseStartDate = null)
|
||||
{
|
||||
$this->m_oDataSource = $oDataSource;
|
||||
|
||||
$this->m_bIsImportPhaseDateKnown = ($oImportPhaseStartDate != null);
|
||||
$this->m_oImportPhaseStartDate = $oImportPhaseStartDate;
|
||||
|
||||
$this->m_oCtx = new ContextTag(ContextTag::TAG_SYNCHRO);
|
||||
$this->m_oCtx1 = new ContextTag('Synchro:'.$oDataSource->GetRawName()); // More precise context information
|
||||
}
|
||||
@@ -3103,7 +3108,7 @@ class SynchroExecution
|
||||
$this->m_oStatLog->Set('stats_nb_replica_total', $this->m_iCountAllReplicas);
|
||||
|
||||
$this->m_oStatLog->DBInsert();
|
||||
$sLastFullLoad = (is_null($this->m_oImportPhaseStartDate)) ? 'not specified' : $this->m_oImportPhaseStartDate->format('Y-m-d H:i:s');
|
||||
$sLastFullLoad = ($this->m_bIsImportPhaseDateKnown) ? $this->m_oImportPhaseStartDate->format('Y-m-d H:i:s') : 'not specified';
|
||||
$this->m_oStatLog->AddTrace("###### STARTING SYNCHRONIZATION ##### Total: {$this->m_iCountAllReplicas} replica(s). Last full load: '$sLastFullLoad' ");
|
||||
$sSql = 'SELECT NOW();';
|
||||
$sDBNow = CMDBSource::QueryToScalar($sSql);
|
||||
@@ -3207,18 +3212,21 @@ class SynchroExecution
|
||||
// Compute and keep track of the limit date taken into account for obsoleting replicas
|
||||
//
|
||||
$iFullLoadInterval = $this->m_oDataSource->Get('full_load_periodicity'); // Duration in seconds
|
||||
if (is_null($this->m_oImportPhaseStartDate)) {
|
||||
if ($this->m_bIsImportPhaseDateKnown) {
|
||||
$oLimitDate = clone $this->m_oImportPhaseStartDate;
|
||||
$sInterval = "-$iFullLoadInterval seconds";
|
||||
$oLimitDate->Modify($sInterval);
|
||||
} else {
|
||||
if ($iFullLoadInterval <= 0) {
|
||||
// we are doing exec phase alone, and the full load interval is set to 0 => we should not update/delete replicas !!
|
||||
// This will prevent actions in DoJob1() method
|
||||
$oLimitDate = new DateTime('1970-01-01');
|
||||
} else {
|
||||
$oLimitDate = self::GetDataBaseCurrentDateTime();
|
||||
$sInterval = "-$iFullLoadInterval seconds";
|
||||
$oLimitDate->Modify($sInterval);
|
||||
}
|
||||
} else {
|
||||
$oLimitDate = clone $this->m_oImportPhaseStartDate;
|
||||
}
|
||||
$this->ExactlySubtractSeconds($oLimitDate, $iFullLoadInterval);
|
||||
$this->m_oLastFullLoadStartDate = $oLimitDate;
|
||||
if ($bFirstPass) {
|
||||
$this->m_oStatLog->AddTrace('Limit Date: '.$this->m_oLastFullLoadStartDate->Format('Y-m-d H:i:s'));
|
||||
@@ -3338,10 +3346,10 @@ class SynchroExecution
|
||||
$aArguments['log'] = $this->m_oStatLog->GetKey();
|
||||
$aArguments['change'] = $this->m_oChange->GetKey();
|
||||
$aArguments['chunk'] = $iMaxChunkSize;
|
||||
if (is_null($this->m_oImportPhaseStartDate)) {
|
||||
$aArguments['last_full_load'] = '';
|
||||
} else {
|
||||
if ($this->m_bIsImportPhaseDateKnown) {
|
||||
$aArguments['last_full_load'] = $this->m_oImportPhaseStartDate->Format('Y-m-d H:i:s');
|
||||
} else {
|
||||
$aArguments['last_full_load'] = '';
|
||||
}
|
||||
|
||||
$this->m_oStatLog->DBUpdate();
|
||||
@@ -3693,11 +3701,13 @@ class SynchroExecution
|
||||
|
||||
// Get all the replicas that are to be deleted
|
||||
//
|
||||
$oDeletionDate = clone $this->m_oLastFullLoadStartDate;
|
||||
$oDeletionDate = $this->m_oLastFullLoadStartDate;
|
||||
$iDeleteRetention = $this->m_oDataSource->Get('delete_policy_retention'); // Duration in seconds
|
||||
$this->ExactlySubtractSeconds($oDeletionDate, $iDeleteRetention);
|
||||
if ($iDeleteRetention > 0) {
|
||||
$sInterval = "-$iDeleteRetention seconds";
|
||||
$oDeletionDate->Modify($sInterval);
|
||||
}
|
||||
$sDeletionDate = $oDeletionDate->Format('Y-m-d H:i:s');
|
||||
|
||||
if ($bFirstPass) {
|
||||
$this->m_oStatLog->AddTrace("Deletion date: $sDeletionDate");
|
||||
}
|
||||
@@ -3747,21 +3757,4 @@ class SynchroExecution
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Take into account timechange to apply date difference operation
|
||||
* @param \DateTime $oDate
|
||||
* @param $iDurationInSeconds
|
||||
* @return void
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function ExactlySubtractSeconds(DateTime $oDate, $iDurationInSeconds): void
|
||||
{
|
||||
if ($iDurationInSeconds > 0) {
|
||||
$oDate->setTimezone(new DateTimeZone('UTC'));
|
||||
$sInterval = "-$iDurationInSeconds seconds";
|
||||
$oDate->Modify($sInterval);
|
||||
$sTimezone = MetaModel::GetConfig()->Get('timezone');
|
||||
$oDate->setTimezone(new DateTimeZone($sTimezone));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
namespace Combodo\iTop\Test\UnitTest\Application;
|
||||
|
||||
use Combodo\iTop\Application\Helper\Session;
|
||||
use Combodo\iTop\Service\Session\Session;
|
||||
use Combodo\iTop\Test\UnitTest\ItopTestCase;
|
||||
|
||||
/**
|
||||
|
||||
@@ -835,8 +835,16 @@ class utilsTest extends ItopTestCase
|
||||
|
||||
public function testFileGetContentsAndMIMETypeOnLocalURL()
|
||||
{
|
||||
$sURL = utils::GetAbsoluteUrlAppRoot().'env-production/itop-request-mgmt/images/user-request.svg';
|
||||
$sPath = APPROOT.'env-production/itop-request-mgmt/images/user-request.svg';
|
||||
// For the test to work on most dev / CI environments, we need to use a module with an SVG image that is installed with the current setup choices
|
||||
if (file_exists(APPROOT.'env-production/itop-request-mgmt/images/user-request.svg')) {
|
||||
$sImgRelPath = 'itop-request-mgmt/images/user-request.svg';
|
||||
} elseif (file_exists(APPROOT.'env-production/itop-request-mgmt-itil/images/user-request.svg')) {
|
||||
$sImgRelPath = 'itop-request-mgmt-itil/images/user-request.svg';
|
||||
}
|
||||
|
||||
$sURL = utils::GetAbsoluteUrlModulesRoot().$sImgRelPath;
|
||||
$sPath = APPROOT.'env-production/'.$sImgRelPath;
|
||||
|
||||
$oExpectedDocument = new ormDocument(file_get_contents($sPath), 'image/svg+xml; charset=us-ascii', 'user-request.svg');
|
||||
$this->assertEquals($oExpectedDocument, utils::FileGetContentsAndMIMEType($sURL));
|
||||
// Read local URL directly on disk
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2026 Combodo SAS
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\Test\UnitTest\Core;
|
||||
|
||||
use Combodo\iTop\Test\UnitTest\ItopDataTestCase;
|
||||
use DBObjectSearch;
|
||||
use DBUnionSearch;
|
||||
use UserRights;
|
||||
|
||||
class DBSearchApplyDataFiltersTest extends ItopDataTestCase
|
||||
{
|
||||
public const CREATE_TEST_ORG = true;
|
||||
|
||||
protected string $sOriginalUserRightsSelectModuleClass = '';
|
||||
|
||||
/**
|
||||
* @throws \Exception
|
||||
*/
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
// Backup original UserRights select module as it will changed in some tests
|
||||
$this->sOriginalUserRightsSelectModuleClass = get_class(UserRights::GetModuleInstance());
|
||||
|
||||
$this->RequireOnceUnitTestFile('Fixtures/N9687_CustomGetSelectFilterClass.php');
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function tearDown(): void
|
||||
{
|
||||
// Restore original UserRights select module to not interfere with next tests
|
||||
UserRights::SelectModule($this->sOriginalUserRightsSelectModuleClass);
|
||||
|
||||
parent::tearDown();
|
||||
}
|
||||
|
||||
public function testApplyDataFiltersOnDBObjectSearchShouldAcceptGetSelectFilterClassReturningDBUnionSearch()
|
||||
{
|
||||
// Use custom select filter that returns a DBUnionSearch
|
||||
$sPreviousSelectModuleClass = get_class(UserRights::GetModuleInstance());
|
||||
UserRights::SelectModule('\\Combodo\\iTop\\Test\\UnitTest\\Core\\Fixtures\\N9687_CustomGetSelectFilterClass');
|
||||
|
||||
// Create a user and login, otherwise the select filter won't apply
|
||||
self::CreateUser('test_dbsearch_applydatafilters', 3);
|
||||
UserRights::Login('test_dbsearch_applydatafilters');
|
||||
|
||||
// Create a person
|
||||
$oCreatedPerson = $this->CreatePerson(microtime());
|
||||
|
||||
// Try to retrieve it using the select filter
|
||||
$oSearch = DBObjectSearch::FromOQL("SELECT Person WHERE id = {$oCreatedPerson->GetKey()}");
|
||||
$oFilteredSearch = $this->InvokeNonPublicMethod(DBObjectSearch::class, 'ApplyDataFilters', $oSearch);
|
||||
|
||||
// Restore original select module to not interfere with next tests
|
||||
UserRights::SelectModule($sPreviousSelectModuleClass);
|
||||
|
||||
$this->assertEquals(DBUnionSearch::class, get_class($oFilteredSearch), "DBObjectSearch::ApplyDataFilters() should be able to return a \DBUnionSearch");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2026 Combodo SAS
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\Test\UnitTest\Core\Fixtures;
|
||||
|
||||
use DBObjectSearch;
|
||||
use DBUnionSearch;
|
||||
use UserRightsProfile;
|
||||
|
||||
class N9687_CustomGetSelectFilterClass extends UserRightsProfile
|
||||
{
|
||||
public function GetSelectFilter($oUser, $sClass, $aSettings = [])
|
||||
{
|
||||
// We just need the method to return an union search
|
||||
return new DBUnionSearch([
|
||||
DBObjectSearch::FromOQL("SELECT $sClass WHERE 1!=2"),
|
||||
DBObjectSearch::FromOQL("SELECT $sClass WHERE 1=1"),
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace Combodo\iTop\Test\UnitTest\Setup;
|
||||
|
||||
use Combodo\iTop\Setup\FeatureRemoval\ModelReflectionSerializer;
|
||||
use Combodo\iTop\Test\UnitTest\ItopTestCase;
|
||||
use SetupUtils;
|
||||
|
||||
@@ -146,4 +147,42 @@ class SetupUtilsTest extends ItopTestCase
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public function testCheckCliPhpVersionIsOk()
|
||||
{
|
||||
$this->RequireOnceItopFile('/setup/feature_removal/ModelReflectionSerializer.php');
|
||||
SetupUtils::CheckCliPhpVersionIsOk(ModelReflectionSerializer::ERROR_LABEL);
|
||||
$this->assertTrue(true);
|
||||
}
|
||||
|
||||
public function testCheckCliPhpVersionFromOutputFail()
|
||||
{
|
||||
$this->RequireOnceItopFile('/setup/feature_removal/ModelReflectionSerializer.php');
|
||||
$sOuput = <<<OUTPUT
|
||||
PHP 7.4.33 (cli) (built: Aug 2 2024 16:22:28) ( NTS )
|
||||
OUTPUT;
|
||||
|
||||
$this->expectException(\CoreException::class);
|
||||
$this->expectExceptionMessage("Data consistency check failed: Mismatch between PHP versions (CLI: 7.4/ UI: 6.6)");
|
||||
$this->InvokeNonPublicStaticMethod(SetupUtils::class, 'CheckCliPhpVersionFromOutput', [ModelReflectionSerializer::ERROR_LABEL, '6.6', 'sPHPExec', [$sOuput]]);
|
||||
}
|
||||
|
||||
public function CheckOKProvider()
|
||||
{
|
||||
return [
|
||||
["7.4 7.2 7.3.33"],
|
||||
["PHP 7.4.33 (cli) (built: Aug 2 2024 16:22:28) ( NTS )"],
|
||||
["PHP 7.4.33 PHP 7.33.22"],
|
||||
["version: 7.4.27 stable"],
|
||||
];
|
||||
}
|
||||
/**
|
||||
* @dataProvider CheckOKProvider
|
||||
*/
|
||||
public function testCheckCliPhpVersionFromOutputOK($sOuput)
|
||||
{
|
||||
$this->RequireOnceItopFile('/setup/feature_removal/ModelReflectionSerializer.php');
|
||||
$this->InvokeNonPublicStaticMethod(SetupUtils::class, 'CheckCliPhpVersionFromOutput', [ModelReflectionSerializer::ERROR_LABEL, '7.4', 'sPHPExec', [$sOuput]]);
|
||||
$this->assertTrue(true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1420,7 +1420,6 @@ HTML,
|
||||
|
||||
$expected = [
|
||||
["class" => "WizStepWelcome","state" => ""],
|
||||
["class" => "WizStepInstallOrUpgrade","state" => ""],
|
||||
["class" => "WizStepDetectedInfo","state" => ""],
|
||||
["class" => "WizStepUpgradeMiscParams","state" => ""],
|
||||
];
|
||||
@@ -1441,7 +1440,6 @@ HTML,
|
||||
|
||||
$expected = [
|
||||
["class" => "WizStepWelcome","state" => ""],
|
||||
["class" => "WizStepInstallOrUpgrade","state" => ""],
|
||||
["class" => "WizStepDetectedInfo","state" => ""],
|
||||
["class" => "WizStepUpgradeMiscParams","state" => ""],
|
||||
];
|
||||
|
||||
@@ -20,10 +20,58 @@ class ModelSerializationTest extends ItopDataTestCase
|
||||
$this->assertEqualsCanonicalizing(MetaModel::GetClasses(), $aModel);
|
||||
}
|
||||
|
||||
public function testGetModelFromEnvironmentFailure()
|
||||
public function testGetModelFromEnvironmentFailure_NoEnvt()
|
||||
{
|
||||
$this->expectException(\CoreException::class);
|
||||
$this->expectExceptionMessage("Cannot get classes");
|
||||
$sEnvDir = APPROOT."env-gabuzomeu";
|
||||
$this->expectExceptionMessage("Data consistency check failed: Missing environment ($sEnvDir)");
|
||||
ModelReflectionSerializer::GetInstance()->GetModelFromEnvironment('gabuzomeu');
|
||||
}
|
||||
|
||||
public function testGetModelFromEnvironmentFailure_NoConfiguration()
|
||||
{
|
||||
$sEnvDir = APPROOT."env-gabuzomeu";
|
||||
$this->aFileToClean [] = $sEnvDir;
|
||||
mkdir($sEnvDir);
|
||||
|
||||
$this->expectException(\CoreException::class);
|
||||
$sConfigFile = APPROOT."conf/gabuzomeu/config-itop.php";
|
||||
$this->expectExceptionMessage("Data consistency check failed: Missing configuration ($sConfigFile)");
|
||||
ModelReflectionSerializer::GetInstance()->GetModelFromEnvironment('gabuzomeu');
|
||||
}
|
||||
|
||||
public function testGetModelFromEnvironmentFailure_BrokenConfiguration()
|
||||
{
|
||||
$sEnvDir = APPROOT."env-gabuzomeu";
|
||||
mkdir($sEnvDir);
|
||||
$this->aFileToClean [] = $sEnvDir;
|
||||
|
||||
mkdir(APPROOT."conf/gabuzomeu");
|
||||
$this->aFileToClean [] = APPROOT."conf/gabuzomeu";
|
||||
$sConfigFile = APPROOT."conf/gabuzomeu/config-itop.php";
|
||||
touch($sConfigFile);
|
||||
file_put_contents($sConfigFile, 'invalid php content...');
|
||||
|
||||
$this->expectException(\CoreException::class);
|
||||
$sError = <<<ERROR
|
||||
Syntax error in configuration file: file = $sConfigFile, error = <tt>invalid php content...</tt>
|
||||
ERROR;
|
||||
|
||||
$this->expectExceptionMessage("Data consistency check failed: $sError");
|
||||
ModelReflectionSerializer::GetInstance()->GetModelFromEnvironment('gabuzomeu');
|
||||
}
|
||||
|
||||
public function testGetModelFromEnvironmentFailure_ItopInMaintenanceMode()
|
||||
{
|
||||
touch(MAINTENANCE_MODE_FILE);
|
||||
$this->aFileToClean [] = MAINTENANCE_MODE_FILE;
|
||||
|
||||
$this->expectException(\CoreException::class);
|
||||
$sError = <<<ERROR
|
||||
This application is currently under maintenance.
|
||||
ERROR;
|
||||
|
||||
$this->expectExceptionMessage("Data consistency check failed: $sError");
|
||||
ModelReflectionSerializer::GetInstance()->GetModelFromEnvironment($this->GetTestEnvironment());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -201,6 +201,7 @@ class DataAuditSequencerTest extends ItopTestCase
|
||||
$oRunTimeEnvironment = $this->createMock(\RunTimeEnvironment::class);
|
||||
$oRunTimeEnvironment->expects($this->once())->method('GetApplicationVersion')
|
||||
->willReturn(['product_version' => ITOP_VERSION_FULL]);
|
||||
$oRunTimeEnvironment->expects($this->once())->method('ExitMaintenanceMode');
|
||||
$oRunTimeEnvironment->expects($this->once())->method('DataToCleanupAudit');
|
||||
$oRunTimeEnvironment->expects($this->any())->method('GetFinalEnv')
|
||||
->willReturn('production');
|
||||
|
||||
@@ -1,55 +0,0 @@
|
||||
<?php
|
||||
|
||||
use Combodo\iTop\Test\UnitTest\ItopDataTestCase;
|
||||
|
||||
class SynchroExecutionTest extends ItopDataTestCase
|
||||
{
|
||||
private function InitAndGetTZ($sTZ = null): string
|
||||
{
|
||||
$sTZ = $sTZ ?? 'Europe/Paris';
|
||||
MetaModel::GetConfig()->Set('timezone', $sTZ);
|
||||
return $sTZ;
|
||||
}
|
||||
|
||||
public function testGetDateMinusRetentionWithTimeChange()
|
||||
{
|
||||
$sTZ = $this->InitAndGetTZ();
|
||||
$oObj = new SynchroExecution($this->createMock(SynchroDataSource::class));
|
||||
$oDate = DateTime::createFromFormat('Y-m-d H:i:s', "2026-03-29 03:30:01", new DateTimeZone($sTZ));
|
||||
|
||||
$oObj->ExactlySubtractSeconds($oDate, 3600);
|
||||
//03h30 minus 1hours => 03h minus 30mn => 03h
|
||||
// => 03h with timechange => 2h
|
||||
// => 02 minus 30mn => 01h30
|
||||
$this->assertEquals("2026-03-29 01:30:01", $oDate->Format('Y-m-d H:i:s'));
|
||||
}
|
||||
|
||||
public function testGetDateMinusRetentioLinuxTimeZero()
|
||||
{
|
||||
$oObj = new SynchroExecution($this->createMock(SynchroDataSource::class));
|
||||
$oDate = new DateTime('1970-01-01');
|
||||
|
||||
$oObj->ExactlySubtractSeconds($oDate, 3600);
|
||||
$this->assertEquals("1969-12-31 23:00:00", $oDate->Format('Y-m-d H:i:s'));
|
||||
}
|
||||
|
||||
public function testGetDateMinusRetentionWithTimeChangeAndUTC()
|
||||
{
|
||||
$sTZ = $this->InitAndGetTZ('UTC');
|
||||
$oObj = new SynchroExecution($this->createMock(SynchroDataSource::class));
|
||||
$oDate = DateTime::createFromFormat('Y-m-d H:i:s', "2026-03-29 03:30:01", new DateTimeZone($sTZ));
|
||||
|
||||
$oObj->ExactlySubtractSeconds($oDate, 3600);
|
||||
$this->assertEquals("2026-03-29 02:30:01", $oDate->Format('Y-m-d H:i:s'));
|
||||
}
|
||||
|
||||
public function testGetDateMinusRetention()
|
||||
{
|
||||
$sTZ = $this->InitAndGetTZ();
|
||||
$oObj = new SynchroExecution($this->createMock(SynchroDataSource::class));
|
||||
$oDate = DateTime::createFromFormat('Y-m-d H:i:s', "2026-04-01 03:30:01", new DateTimeZone($sTZ));
|
||||
|
||||
$oObj->ExactlySubtractSeconds($oDate, 3600);
|
||||
$this->assertEquals("2026-04-01 02:30:01", $oDate->Format('Y-m-d H:i:s'));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user