mirror of
https://github.com/Combodo/iTop.git
synced 2026-02-13 07:24:13 +01:00
N°8763 Halt setup if non-uninstallable extension is missing (#781)
* N°8763 Halt setup if an installed & non-uninstallable extension is missing from disk
This commit is contained in:
@@ -60,6 +60,11 @@ class iTopExtension
|
||||
* @var bool
|
||||
*/
|
||||
public $bMarkedAsChosen;
|
||||
/**
|
||||
* If null, check if at least one module cannot be uninstalled
|
||||
* @var bool|null
|
||||
*/
|
||||
public ?bool $bCanBeUninstalled = null;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
@@ -91,6 +96,14 @@ class iTopExtension
|
||||
* @var string[]
|
||||
*/
|
||||
public $aMissingDependencies;
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
public bool $bInstalled = false;
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
public bool $bRemovedFromDisk = false;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
@@ -115,13 +128,14 @@ class iTopExtension
|
||||
* @since 3.3.0
|
||||
* @return bool
|
||||
*/
|
||||
public function CanBeUninstalled()
|
||||
public function CanBeUninstalled(): bool
|
||||
{
|
||||
if (!is_null($this->bCanBeUninstalled)) {
|
||||
return $this->bCanBeUninstalled;
|
||||
}
|
||||
foreach ($this->aModuleInfo as $sModuleCode => $aModuleInfo) {
|
||||
$bUninstallable = $aModuleInfo['uninstallable'] === 'yes';
|
||||
if (!$bUninstallable) {
|
||||
return false;
|
||||
}
|
||||
$this->bCanBeUninstalled = $aModuleInfo['uninstallable'] === 'yes';
|
||||
return $this->bCanBeUninstalled;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -139,6 +153,11 @@ class iTopExtensionsMap
|
||||
* @return void
|
||||
*/
|
||||
protected $aExtensions;
|
||||
/**
|
||||
* The list of all currently installed extensions
|
||||
* @var array|null
|
||||
*/
|
||||
protected ?array $aInstalledExtensions = null;
|
||||
|
||||
/**
|
||||
* The list of directories browsed using the ReadDir method when building the map
|
||||
@@ -146,7 +165,7 @@ class iTopExtensionsMap
|
||||
*/
|
||||
protected $aScannedDirs;
|
||||
|
||||
public function __construct($sFromEnvironment = 'production', $bNormalizeOldExtensions = true, $aExtraDirs = [])
|
||||
public function __construct($sFromEnvironment = 'production', $aExtraDirs = [])
|
||||
{
|
||||
$this->aExtensions = [];
|
||||
$this->aScannedDirs = [];
|
||||
@@ -155,9 +174,6 @@ class iTopExtensionsMap
|
||||
$this->ReadDir($sDir, iTopExtension::SOURCE_REMOTE);
|
||||
}
|
||||
$this->CheckDependencies($sFromEnvironment);
|
||||
if ($bNormalizeOldExtensions) {
|
||||
$this->NormalizeOldExtensions();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -213,6 +229,7 @@ class iTopExtensionsMap
|
||||
$oExtension = new iTopExtension();
|
||||
$oExtension->sCode = $aChoiceInfo['extension_code'];
|
||||
$oExtension->sLabel = $aChoiceInfo['title'];
|
||||
$oExtension->sDescription = $aChoiceInfo['description'];
|
||||
if (array_key_exists('modules', $aChoiceInfo)) {
|
||||
// Some wizard choices are not associated with any module
|
||||
$oExtension->aModules = $aChoiceInfo['modules'];
|
||||
@@ -261,7 +278,7 @@ class iTopExtensionsMap
|
||||
*
|
||||
* @return \iTopExtension|null
|
||||
*/
|
||||
public function Get(string $sExtensionCode): ?iTopExtension
|
||||
public function GetFromExtensionCode(string $sExtensionCode): ?iTopExtension
|
||||
{
|
||||
foreach ($this->aExtensions as $oExtension) {
|
||||
if ($oExtension->sCode === $sExtensionCode) {
|
||||
@@ -341,7 +358,7 @@ class iTopExtensionsMap
|
||||
$this->aExtensions[$sParentExtensionId]->aModuleVersion[$sModuleName] = $sModuleVersion;
|
||||
$this->aExtensions[$sParentExtensionId]->aModuleInfo[$sModuleName] = $aModuleInfo[ModuleFileReader::MODULE_INFO_CONFIG];
|
||||
} else {
|
||||
// Not already inside an folder containing an 'extension.xml' file
|
||||
// Not already inside a folder containing an 'extension.xml' file
|
||||
|
||||
// Ignore non-visible modules and auto-select ones, since these are never prompted
|
||||
// as a choice to the end-user
|
||||
@@ -432,10 +449,18 @@ class iTopExtensionsMap
|
||||
return $this->aExtensions;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array All available extensions and extensions currently installed but not available due to files removal
|
||||
*/
|
||||
public function GetAllExtensionsWithPreviouslyInstalled(): array
|
||||
{
|
||||
return array_merge($this->aExtensions, $this->aInstalledExtensions ?? []);
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark the given extension as chosen
|
||||
* @param string $sExtensionCode The code of the extension (code without verison number)
|
||||
* @param bool $bMark The value to set for the bmarkAschosen flag
|
||||
* @param string $sExtensionCode The code of the extension (code without version number)
|
||||
* @param bool $bMark The value to set for the bMarkAsChosen flag
|
||||
* @return void
|
||||
*/
|
||||
public function MarkAsChosen($sExtensionCode, $bMark = true)
|
||||
@@ -501,125 +526,56 @@ class iTopExtensionsMap
|
||||
*/
|
||||
public function LoadChoicesFromDatabase(Config $oConfig)
|
||||
{
|
||||
try {
|
||||
$aInstalledExtensions = [];
|
||||
if (CMDBSource::DBName() === null) {
|
||||
CMDBSource::InitFromConfig($oConfig);
|
||||
}
|
||||
$sLatestInstallationDate = CMDBSource::QueryToScalar("SELECT max(installed) FROM ".$oConfig->Get('db_subname')."priv_extension_install");
|
||||
$aInstalledExtensions = CMDBSource::QueryToArray("SELECT * FROM ".$oConfig->Get('db_subname')."priv_extension_install WHERE installed = '".$sLatestInstallationDate."'");
|
||||
} catch (MySQLException $e) {
|
||||
// No database or erroneous information
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach ($aInstalledExtensions as $aDBInfo) {
|
||||
$this->MarkAsChosen($aDBInfo['code']);
|
||||
$this->SetInstalledVersion($aDBInfo['code'], $aDBInfo['version']);
|
||||
foreach ($this->LoadInstalledExtensionsFromDatabase($oConfig) as $oExtension) {
|
||||
$this->MarkAsChosen($oExtension->sCode);
|
||||
$this->SetInstalledVersion($oExtension->sCode, $oExtension->sVersion);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find is a single-module extension is contained within another extension
|
||||
* @param iTopExtension $oExtension
|
||||
* @return NULL|iTopExtension
|
||||
*/
|
||||
public function IsExtensionObsoletedByAnother(iTopExtension $oExtension)
|
||||
protected function LoadInstalledExtensionsFromDatabase(Config $oConfig): array|false
|
||||
{
|
||||
// Complex extensions (more than 1 module) are never considered as obsolete
|
||||
if (count($oExtension->aModules) != 1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
foreach ($this->GetAllExtensions() as $oOtherExtension) {
|
||||
if (($oOtherExtension->sSourceDir != $oExtension->sSourceDir) && ($oOtherExtension->sSource != iTopExtension::SOURCE_WIZARD)) {
|
||||
if (array_key_exists($oExtension->sCode, $oOtherExtension->aModuleVersion) &&
|
||||
(version_compare($oOtherExtension->aModuleVersion[$oExtension->sCode], $oExtension->sVersion, '>='))) {
|
||||
// Found another extension containing a more recent version of the extension/module
|
||||
return $oOtherExtension;
|
||||
}
|
||||
try {
|
||||
if (CMDBSource::DBName() === null) {
|
||||
CMDBSource::InitFromConfig($oConfig);
|
||||
}
|
||||
}
|
||||
$sLatestInstallationDate = CMDBSource::QueryToScalar("SELECT max(installed) FROM ".$oConfig->Get('db_subname')."priv_extension_install");
|
||||
$aDBInfo = CMDBSource::QueryToArray("SELECT * FROM ".$oConfig->Get('db_subname')."priv_extension_install WHERE installed = '".$sLatestInstallationDate."'");
|
||||
|
||||
// No match at all
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Search for multi-module extensions that are NOT deployed as an extension (i.e. shipped with an extension.xml file)
|
||||
* but as a bunch of un-related modules based on the signature of some well-known extensions. If such an extension is found,
|
||||
* replace the stand-alone modules by an "extension" with the appropriate label/description/version containing the same modules.
|
||||
* @param string $sInSourceOnly The source directory to scan (datamodel|extensions|data)
|
||||
*/
|
||||
public function NormalizeOldExtensions($sInSourceOnly = iTopExtension::SOURCE_MANUAL)
|
||||
{
|
||||
$aSignatures = $this->GetOldExtensionsSignatures();
|
||||
foreach ($aSignatures as $sExtensionCode => $aExtensionSignatures) {
|
||||
$bFound = false;
|
||||
foreach ($aExtensionSignatures['versions'] as $sVersion => $aModules) {
|
||||
$bInstalled = true;
|
||||
foreach ($aModules as $sModuleId) {
|
||||
if (!$this->ModuleIsPresent($sModuleId, $sInSourceOnly)) {
|
||||
$bFound = false;
|
||||
break; // One missing module is enough to determine that the extension/version is not present
|
||||
} else {
|
||||
$bInstalled = $bInstalled && $this->ModuleIsInstalled($sModuleId, $sInSourceOnly);
|
||||
$bFound = true;
|
||||
}
|
||||
}
|
||||
if ($bFound) {
|
||||
break;
|
||||
} // The current version matches the signature
|
||||
}
|
||||
|
||||
if ($bFound) {
|
||||
$this->aInstalledExtensions = [];
|
||||
foreach ($aDBInfo as $aExtensionInfo) {
|
||||
$oExtension = new iTopExtension();
|
||||
$oExtension->sCode = $sExtensionCode;
|
||||
$oExtension->sLabel = $aExtensionSignatures['label'];
|
||||
$oExtension->sSource = $sInSourceOnly;
|
||||
$oExtension->sDescription = $aExtensionSignatures['description'];
|
||||
$oExtension->sVersion = $sVersion;
|
||||
$oExtension->sCode = $aExtensionInfo['code'];
|
||||
$oExtension->sLabel = $aExtensionInfo['label'];
|
||||
$oExtension->sDescription = $aExtensionInfo['description'] ?? '';
|
||||
$oExtension->sVersion = $aExtensionInfo['version'];
|
||||
$oExtension->sSource = $aExtensionInfo['source'];
|
||||
$oExtension->bMandatory = false;
|
||||
$oExtension->sMoreInfoUrl = '';
|
||||
$oExtension->aModules = [];
|
||||
if ($bInstalled) {
|
||||
$oExtension->sInstalledVersion = $sVersion;
|
||||
$oExtension->bMarkedAsChosen = true;
|
||||
$oExtension->aModuleVersion = [];
|
||||
$oExtension->aModuleInfo = [];
|
||||
$oExtension->sSourceDir = '';
|
||||
$oExtension->bVisible = true;
|
||||
$oExtension->bInstalled = true;
|
||||
$oExtension->bCanBeUninstalled = !isset($aExtensionInfo['uninstallable']) || $aExtensionInfo['uninstallable'] === 'yes';
|
||||
$oChoice = $this->GetFromExtensionCode($oExtension->sCode);
|
||||
if ($oChoice) {
|
||||
$oChoice->bInstalled = true;
|
||||
} else {
|
||||
$oExtension->bRemovedFromDisk = true;
|
||||
}
|
||||
foreach ($aModules as $sModuleId) {
|
||||
list($sModuleName, $sModuleVersion) = ModuleDiscovery::GetModuleName($sModuleId);
|
||||
$oExtension->aModules[] = $sModuleName;
|
||||
$oExtension->aModuleInfo[$sModuleName] = $this->aExtensions[$sModuleId]->aModuleInfo[$sModuleName];
|
||||
}
|
||||
$this->ReplaceModulesByNormalizedExtension($aExtensionSignatures['versions'][$sVersion], $oExtension);
|
||||
|
||||
$this->aInstalledExtensions[$oExtension->sCode.'/'.$oExtension->sVersion] = $oExtension;
|
||||
}
|
||||
|
||||
return $this->aInstalledExtensions;
|
||||
} catch (MySQLException $e) {
|
||||
// No database or erroneous information
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the given module-code/version is present on the disk
|
||||
* @param string $sModuleIdToFind The module ID (code/version) to search for
|
||||
* @param string $sInSourceOnly The origin (=source) to search in (datamodel|extensions|data)
|
||||
* @return boolean
|
||||
*/
|
||||
protected function ModuleIsPresent($sModuleIdToFind, $sInSourceOnly)
|
||||
{
|
||||
return (array_key_exists($sModuleIdToFind, $this->aExtensions) && ($this->aExtensions[$sModuleIdToFind]->sSource == $sInSourceOnly));
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the given module-code/version is currently installed
|
||||
* @param string $sModuleIdToFind The module ID (code/version) to search for
|
||||
* @param string $sInSourceOnly The origin (=source) to search in (datamodel|extensions|data)
|
||||
* @return boolean
|
||||
*/
|
||||
protected function ModuleIsInstalled($sModuleIdToFind, $sInSourceOnly)
|
||||
{
|
||||
return (array_key_exists($sModuleIdToFind, $this->aExtensions) &&
|
||||
($this->aExtensions[$sModuleIdToFind]->sSource == $sInSourceOnly) &&
|
||||
($this->aExtensions[$sModuleIdToFind]->sInstalledVersion !== ''));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells if the given module name is "chosen" since it is part of a "chosen" extension (in the specified source dir)
|
||||
* @param string $sModuleNameToFind
|
||||
@@ -640,657 +596,4 @@ class iTopExtensionsMap
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace a given set of stand-alone modules by one single "extension"
|
||||
* @param string[] $aModules
|
||||
* @param iTopExtension $oNewExtension
|
||||
*/
|
||||
protected function ReplaceModulesByNormalizedExtension($aModules, iTopExtension $oNewExtension)
|
||||
{
|
||||
foreach ($aModules as $sModuleId) {
|
||||
unset($this->aExtensions[$sModuleId]);
|
||||
}
|
||||
$this->AddExtension($oNewExtension);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of signatures of some well-known multi-module extensions without extension.xml file (should not exist anymore)
|
||||
*
|
||||
* @return string[][]|string[][][][]
|
||||
*/
|
||||
protected function GetOldExtensionsSignatures()
|
||||
{
|
||||
// Generated by the Factory using the page export_component_versions_for_normalisation.php
|
||||
return [
|
||||
'combodo-approval-process-light' =>
|
||||
[
|
||||
'label' => 'Approval process light',
|
||||
'description' => 'Approve a request via a simple email',
|
||||
'versions' =>
|
||||
[
|
||||
'1.0.1' =>
|
||||
[
|
||||
0 => 'approval-base/2.1.0',
|
||||
1 => 'combodo-approval-light/1.0.1',
|
||||
],
|
||||
'1.0.2' =>
|
||||
[
|
||||
0 => 'approval-base/2.1.1',
|
||||
1 => 'combodo-approval-light/1.0.2',
|
||||
],
|
||||
'1.0.3' =>
|
||||
[
|
||||
0 => 'approval-base/2.1.2',
|
||||
1 => 'combodo-approval-light/1.0.2',
|
||||
],
|
||||
'1.1.0' =>
|
||||
[
|
||||
0 => 'approval-base/2.2.2',
|
||||
1 => 'combodo-approval-light/1.0.2',
|
||||
],
|
||||
'1.1.1' =>
|
||||
[
|
||||
0 => 'approval-base/2.2.3',
|
||||
1 => 'combodo-approval-light/1.0.2',
|
||||
],
|
||||
'1.1.2' =>
|
||||
[
|
||||
0 => 'approval-base/2.2.6',
|
||||
1 => 'combodo-approval-light/1.0.2',
|
||||
],
|
||||
'1.1.3' =>
|
||||
[
|
||||
0 => 'approval-base/2.2.6',
|
||||
1 => 'combodo-approval-light/1.0.3',
|
||||
],
|
||||
'1.2.0' =>
|
||||
[
|
||||
0 => 'approval-base/2.3.0',
|
||||
1 => 'combodo-approval-light/1.0.3',
|
||||
],
|
||||
'1.2.1' =>
|
||||
[
|
||||
0 => 'approval-base/2.4.0',
|
||||
1 => 'combodo-approval-light/1.0.4',
|
||||
],
|
||||
'1.3.0' =>
|
||||
[
|
||||
0 => 'approval-base/2.4.2',
|
||||
1 => 'combodo-approval-light/1.1.1',
|
||||
],
|
||||
'1.3.1' =>
|
||||
[
|
||||
0 => 'approval-base/2.5.0',
|
||||
1 => 'combodo-approval-light/1.1.1',
|
||||
],
|
||||
'1.3.2' =>
|
||||
[
|
||||
0 => 'approval-base/2.5.0',
|
||||
1 => 'combodo-approval-light/1.1.2',
|
||||
],
|
||||
'1.2.2' =>
|
||||
[
|
||||
0 => 'approval-base/2.4.2',
|
||||
1 => 'combodo-approval-light/1.0.5',
|
||||
],
|
||||
'1.3.3' =>
|
||||
[
|
||||
0 => 'approval-base/2.5.1',
|
||||
1 => 'combodo-approval-light/1.1.2',
|
||||
],
|
||||
'1.3.4' =>
|
||||
[
|
||||
0 => 'approval-base/2.5.2',
|
||||
1 => 'combodo-approval-light/1.1.2',
|
||||
],
|
||||
'1.3.5' =>
|
||||
[
|
||||
0 => 'approval-base/2.5.3',
|
||||
1 => 'combodo-approval-light/1.1.2',
|
||||
],
|
||||
'1.4.0' =>
|
||||
[
|
||||
0 => 'approval-base/2.5.3',
|
||||
1 => 'combodo-approval-light/1.1.2',
|
||||
2 => 'itop-approval-portal/1.0.0',
|
||||
],
|
||||
],
|
||||
],
|
||||
'combodo-approval-process-automation' =>
|
||||
[
|
||||
'label' => 'Approval process automation',
|
||||
'description' => 'Control your approval process with predefined rules based on service catalog',
|
||||
'versions' =>
|
||||
[
|
||||
'1.0.1' =>
|
||||
[
|
||||
0 => 'approval-base/2.1.0',
|
||||
1 => 'combodo-approval-extended/1.0.2',
|
||||
],
|
||||
'1.0.2' =>
|
||||
[
|
||||
0 => 'approval-base/2.1.1',
|
||||
1 => 'combodo-approval-extended/1.0.4',
|
||||
],
|
||||
'1.0.3' =>
|
||||
[
|
||||
0 => 'approval-base/2.1.2',
|
||||
1 => 'combodo-approval-extended/1.0.4',
|
||||
],
|
||||
'1.1.0' =>
|
||||
[
|
||||
0 => 'approval-base/2.2.2',
|
||||
1 => 'combodo-approval-extended/1.0.4',
|
||||
],
|
||||
'1.1.1' =>
|
||||
[
|
||||
0 => 'approval-base/2.2.3',
|
||||
1 => 'combodo-approval-extended/1.0.4',
|
||||
],
|
||||
'1.1.2' =>
|
||||
[
|
||||
0 => 'approval-base/2.2.6',
|
||||
1 => 'combodo-approval-extended/1.0.5',
|
||||
],
|
||||
'1.1.3' =>
|
||||
[
|
||||
0 => 'approval-base/2.2.6',
|
||||
1 => 'combodo-approval-extended/1.0.6',
|
||||
],
|
||||
'1.2.0' =>
|
||||
[
|
||||
0 => 'approval-base/2.3.0',
|
||||
1 => 'combodo-approval-extended/1.0.7',
|
||||
],
|
||||
'1.2.1' =>
|
||||
[
|
||||
0 => 'approval-base/2.4.0',
|
||||
1 => 'combodo-approval-extended/1.0.8',
|
||||
],
|
||||
'1.3.0' =>
|
||||
[
|
||||
0 => 'approval-base/2.4.2',
|
||||
1 => 'combodo-approval-extended/1.2.1',
|
||||
],
|
||||
'1.3.1' =>
|
||||
[
|
||||
0 => 'approval-base/2.5.0',
|
||||
1 => 'combodo-approval-extended/1.2.1',
|
||||
],
|
||||
'1.3.2' =>
|
||||
[
|
||||
0 => 'approval-base/2.5.0',
|
||||
1 => 'combodo-approval-extended/1.2.2',
|
||||
],
|
||||
'1.2.2' =>
|
||||
[
|
||||
0 => 'approval-base/2.4.2',
|
||||
1 => 'combodo-approval-extended/1.0.9',
|
||||
],
|
||||
'1.3.3' =>
|
||||
[
|
||||
0 => 'approval-base/2.5.1',
|
||||
1 => 'combodo-approval-extended/1.2.3',
|
||||
],
|
||||
'1.3.4' =>
|
||||
[
|
||||
0 => 'approval-base/2.5.2',
|
||||
1 => 'combodo-approval-extended/1.2.3',
|
||||
],
|
||||
'1.3.5' =>
|
||||
[
|
||||
0 => 'approval-base/2.5.3',
|
||||
1 => 'combodo-approval-extended/1.2.3',
|
||||
],
|
||||
'1.4.0' =>
|
||||
[
|
||||
0 => 'approval-base/2.5.3',
|
||||
1 => 'combodo-approval-extended/1.2.3',
|
||||
3 => 'itop-approval-portal/1.0.0',
|
||||
],
|
||||
],
|
||||
],
|
||||
'combodo-predefined-response-models' =>
|
||||
[
|
||||
'label' => 'Predefined response models',
|
||||
'description' => 'Pick common answers from a list of predefined replies grouped by categories to update tickets log',
|
||||
'versions' =>
|
||||
[
|
||||
'1.0.0' =>
|
||||
[
|
||||
0 => 'precanned-replies/1.0.0',
|
||||
1 => 'precanned-replies-pro/1.0.0',
|
||||
],
|
||||
'1.0.1' =>
|
||||
[
|
||||
0 => 'precanned-replies/1.0.1',
|
||||
1 => 'precanned-replies-pro/1.0.1',
|
||||
],
|
||||
'1.0.2' =>
|
||||
[
|
||||
0 => 'precanned-replies/1.0.2',
|
||||
1 => 'precanned-replies-pro/1.0.1',
|
||||
],
|
||||
'1.0.3' =>
|
||||
[
|
||||
0 => 'precanned-replies/1.0.3',
|
||||
1 => 'precanned-replies-pro/1.0.1',
|
||||
],
|
||||
'1.0.4' =>
|
||||
[
|
||||
0 => 'precanned-replies/1.0.3',
|
||||
1 => 'precanned-replies-pro/1.0.2',
|
||||
],
|
||||
'1.0.5' =>
|
||||
[
|
||||
0 => 'precanned-replies/1.0.4',
|
||||
1 => 'precanned-replies-pro/1.0.2',
|
||||
],
|
||||
'1.1.0' =>
|
||||
[
|
||||
0 => 'precanned-replies/1.1.0',
|
||||
1 => 'precanned-replies-pro/1.0.2',
|
||||
],
|
||||
'1.1.1' =>
|
||||
[
|
||||
0 => 'precanned-replies/1.1.1',
|
||||
1 => 'precanned-replies-pro/1.0.2',
|
||||
],
|
||||
],
|
||||
],
|
||||
'combodo-customized-request-forms' =>
|
||||
[
|
||||
'label' => 'Customized request forms',
|
||||
'description' => 'Define personalized request forms based on the service catalog. Add extra fields for a given type of request.',
|
||||
'versions' =>
|
||||
[
|
||||
'1.0.1' =>
|
||||
[
|
||||
0 => 'templates-base/2.1.1',
|
||||
1 => 'itop-request-template/1.0.0',
|
||||
],
|
||||
'1.0.2' =>
|
||||
[
|
||||
0 => 'templates-base/2.1.2',
|
||||
1 => 'itop-request-template/1.0.0',
|
||||
],
|
||||
'1.0.3' =>
|
||||
[
|
||||
0 => 'templates-base/2.1.2',
|
||||
1 => 'itop-request-template/1.0.1',
|
||||
],
|
||||
'1.0.4' =>
|
||||
[
|
||||
0 => 'templates-base/2.1.3',
|
||||
1 => 'itop-request-template/1.0.1',
|
||||
],
|
||||
'1.0.5' =>
|
||||
[
|
||||
0 => 'templates-base/2.1.4',
|
||||
1 => 'itop-request-template/1.0.1',
|
||||
],
|
||||
'2.0.0' =>
|
||||
[
|
||||
0 => 'templates-base/3.0.0',
|
||||
1 => 'itop-request-template/2.0.0',
|
||||
2 => 'itop-request-template-portal/1.0.0',
|
||||
],
|
||||
'2.0.1' =>
|
||||
[
|
||||
0 => 'templates-base/3.0.1',
|
||||
1 => 'itop-request-template/2.0.0',
|
||||
2 => 'itop-request-template-portal/1.0.0',
|
||||
],
|
||||
'2.0.2' =>
|
||||
[
|
||||
0 => 'templates-base/3.0.2',
|
||||
1 => 'itop-request-template/2.0.0',
|
||||
2 => 'itop-request-template-portal/1.0.0',
|
||||
],
|
||||
'2.0.3' =>
|
||||
[
|
||||
0 => 'templates-base/3.0.4',
|
||||
1 => 'itop-request-template/2.0.0',
|
||||
2 => 'itop-request-template-portal/1.0.0',
|
||||
],
|
||||
'2.0.4' =>
|
||||
[
|
||||
0 => 'templates-base/3.0.5',
|
||||
1 => 'itop-request-template/2.0.0',
|
||||
2 => 'itop-request-template-portal/1.0.0',
|
||||
],
|
||||
'2.0.5' =>
|
||||
[
|
||||
0 => 'templates-base/3.0.6',
|
||||
1 => 'itop-request-template/2.0.0',
|
||||
2 => 'itop-request-template-portal/1.0.0',
|
||||
],
|
||||
'2.0.6' =>
|
||||
[
|
||||
0 => 'templates-base/3.0.8',
|
||||
1 => 'itop-request-template/2.0.0',
|
||||
2 => 'itop-request-template-portal/1.0.0',
|
||||
],
|
||||
'2.0.7' =>
|
||||
[
|
||||
0 => 'templates-base/3.0.9',
|
||||
1 => 'itop-request-template/2.0.0',
|
||||
2 => 'itop-request-template-portal/1.0.0',
|
||||
],
|
||||
'2.0.8' =>
|
||||
[
|
||||
0 => 'templates-base/3.0.12',
|
||||
1 => 'itop-request-template/2.0.0',
|
||||
2 => 'itop-request-template-portal/1.0.0',
|
||||
],
|
||||
],
|
||||
],
|
||||
'combodo-sla-considering-business-hours' =>
|
||||
[
|
||||
'label' => 'SLA considering business hours',
|
||||
'description' => 'Compute SLAs taking into account service coverage window and holidays',
|
||||
'versions' =>
|
||||
[
|
||||
'2.0.1' =>
|
||||
[
|
||||
0 => 'combodo-sla-computation/2.0.1',
|
||||
1 => 'combodo-coverage-windows-computation/2.0.0',
|
||||
],
|
||||
'2.1.0' =>
|
||||
[
|
||||
0 => 'combodo-sla-computation/2.1.0',
|
||||
1 => 'combodo-coverage-windows-computation/2.0.0',
|
||||
],
|
||||
'2.1.1' =>
|
||||
[
|
||||
0 => 'combodo-sla-computation/2.1.1',
|
||||
1 => 'combodo-coverage-windows-computation/2.0.0',
|
||||
],
|
||||
'2.1.2' =>
|
||||
[
|
||||
0 => 'combodo-sla-computation/2.1.2',
|
||||
1 => 'combodo-coverage-windows-computation/2.0.0',
|
||||
],
|
||||
'2.1.3' =>
|
||||
[
|
||||
0 => 'combodo-sla-computation/2.1.2',
|
||||
1 => 'combodo-coverage-windows-computation/2.0.1',
|
||||
],
|
||||
'2.0.2' =>
|
||||
[
|
||||
0 => 'combodo-sla-computation/2.0.1',
|
||||
1 => 'combodo-coverage-windows-computation/2.0.1',
|
||||
],
|
||||
'2.1.4' =>
|
||||
[
|
||||
0 => 'combodo-sla-computation/2.1.3',
|
||||
1 => 'combodo-coverage-windows-computation/2.0.1',
|
||||
],
|
||||
'2.1.5' =>
|
||||
[
|
||||
0 => 'combodo-sla-computation/2.1.5',
|
||||
1 => 'combodo-coverage-windows-computation/2.0.1',
|
||||
],
|
||||
'2.1.6' =>
|
||||
[
|
||||
0 => 'combodo-sla-computation/2.1.5',
|
||||
1 => 'combodo-coverage-windows-computation/2.0.2',
|
||||
],
|
||||
'2.1.7' =>
|
||||
[
|
||||
0 => 'combodo-sla-computation/2.1.6',
|
||||
1 => 'combodo-coverage-windows-computation/2.0.2',
|
||||
],
|
||||
'2.1.8' =>
|
||||
[
|
||||
0 => 'combodo-sla-computation/2.1.7',
|
||||
1 => 'combodo-coverage-windows-computation/2.0.2',
|
||||
],
|
||||
'2.1.9' =>
|
||||
[
|
||||
0 => 'combodo-sla-computation/2.1.8',
|
||||
1 => 'combodo-coverage-windows-computation/2.0.2',
|
||||
],
|
||||
],
|
||||
],
|
||||
'combodo-mail-to-ticket-automation' =>
|
||||
[
|
||||
'label' => 'Mail to ticket automation',
|
||||
'description' => 'Scan several mailboxes to create or update tickets.',
|
||||
'versions' =>
|
||||
[
|
||||
'2.6.0' =>
|
||||
[
|
||||
0 => 'combodo-email-synchro/2.6.0',
|
||||
1 => 'itop-standard-email-synchro/2.6.0',
|
||||
],
|
||||
'2.6.1' =>
|
||||
[
|
||||
0 => 'combodo-email-synchro/2.6.1',
|
||||
1 => 'itop-standard-email-synchro/2.6.0',
|
||||
],
|
||||
'2.6.2' =>
|
||||
[
|
||||
0 => 'combodo-email-synchro/2.6.2',
|
||||
1 => 'itop-standard-email-synchro/2.6.0',
|
||||
],
|
||||
'2.6.3' =>
|
||||
[
|
||||
0 => 'combodo-email-synchro/2.6.2',
|
||||
1 => 'itop-standard-email-synchro/2.6.1',
|
||||
],
|
||||
'2.6.4' =>
|
||||
[
|
||||
0 => 'combodo-email-synchro/2.6.3',
|
||||
1 => 'itop-standard-email-synchro/2.6.2',
|
||||
],
|
||||
'2.6.5' =>
|
||||
[
|
||||
0 => 'combodo-email-synchro/2.6.4',
|
||||
1 => 'itop-standard-email-synchro/2.6.2',
|
||||
],
|
||||
'2.6.6' =>
|
||||
[
|
||||
0 => 'combodo-email-synchro/2.6.5',
|
||||
1 => 'itop-standard-email-synchro/2.6.3',
|
||||
],
|
||||
'2.6.7' =>
|
||||
[
|
||||
0 => 'combodo-email-synchro/2.6.6',
|
||||
1 => 'itop-standard-email-synchro/2.6.4',
|
||||
],
|
||||
'2.6.8' =>
|
||||
[
|
||||
0 => 'combodo-email-synchro/2.6.7',
|
||||
1 => 'itop-standard-email-synchro/2.6.4',
|
||||
],
|
||||
'2.6.9' =>
|
||||
[
|
||||
0 => 'combodo-email-synchro/2.6.8',
|
||||
1 => 'itop-standard-email-synchro/2.6.5',
|
||||
],
|
||||
'2.6.10' =>
|
||||
[
|
||||
0 => 'combodo-email-synchro/2.6.9',
|
||||
1 => 'itop-standard-email-synchro/2.6.6',
|
||||
],
|
||||
'2.6.11' =>
|
||||
[
|
||||
0 => 'combodo-email-synchro/2.6.10',
|
||||
1 => 'itop-standard-email-synchro/2.6.6',
|
||||
],
|
||||
'2.6.12' =>
|
||||
[
|
||||
0 => 'combodo-email-synchro/2.6.11',
|
||||
1 => 'itop-standard-email-synchro/2.6.6',
|
||||
],
|
||||
'3.0.0' =>
|
||||
[
|
||||
0 => 'combodo-email-synchro/3.0.0',
|
||||
1 => 'itop-standard-email-synchro/3.0.0',
|
||||
],
|
||||
'3.0.1' =>
|
||||
[
|
||||
0 => 'combodo-email-synchro/3.0.1',
|
||||
1 => 'itop-standard-email-synchro/3.0.1',
|
||||
],
|
||||
'3.0.2' =>
|
||||
[
|
||||
0 => 'combodo-email-synchro/3.0.2',
|
||||
1 => 'itop-standard-email-synchro/3.0.1',
|
||||
],
|
||||
'3.0.3' =>
|
||||
[
|
||||
0 => 'combodo-email-synchro/3.0.3',
|
||||
1 => 'itop-standard-email-synchro/3.0.3',
|
||||
],
|
||||
'3.0.4' =>
|
||||
[
|
||||
0 => 'combodo-email-synchro/3.0.3',
|
||||
1 => 'itop-standard-email-synchro/3.0.4',
|
||||
],
|
||||
'3.0.5' =>
|
||||
[
|
||||
0 => 'combodo-email-synchro/3.0.4',
|
||||
1 => 'itop-standard-email-synchro/3.0.4',
|
||||
],
|
||||
'3.0.6' =>
|
||||
[
|
||||
0 => 'combodo-email-synchro/3.0.5',
|
||||
1 => 'itop-standard-email-synchro/3.0.4',
|
||||
],
|
||||
'3.0.7' =>
|
||||
[
|
||||
0 => 'combodo-email-synchro/3.0.5',
|
||||
1 => 'itop-standard-email-synchro/3.0.5',
|
||||
],
|
||||
],
|
||||
],
|
||||
'combodo-configurator-for-automatic-object-creation' =>
|
||||
[
|
||||
'label' => 'Configurator for automatic object creation',
|
||||
'description' => 'Templating based on existing objects.',
|
||||
'versions' =>
|
||||
[
|
||||
'1.0.13' =>
|
||||
[
|
||||
1 => 'itop-stencils/1.0.6',
|
||||
],
|
||||
],
|
||||
],
|
||||
'combodo-user-actions-configurator' =>
|
||||
[
|
||||
'label' => 'User actions configurator',
|
||||
'description' => 'Configure user actions to simplify and automate processes (e.g. create an incident from a CI).',
|
||||
'versions' =>
|
||||
[
|
||||
'1.0.0' =>
|
||||
[
|
||||
0 => 'itop-object-copier/1.0.0',
|
||||
],
|
||||
'1.0.1' =>
|
||||
[
|
||||
0 => 'itop-object-copier/1.0.1',
|
||||
],
|
||||
'1.0.2' =>
|
||||
[
|
||||
0 => 'itop-object-copier/1.0.2',
|
||||
],
|
||||
'1.0.3' =>
|
||||
[
|
||||
0 => 'itop-object-copier/1.0.3',
|
||||
],
|
||||
'1.1.0' =>
|
||||
[
|
||||
0 => 'itop-object-copier/1.1.0',
|
||||
],
|
||||
'1.1.1' =>
|
||||
[
|
||||
0 => 'itop-object-copier/1.1.1',
|
||||
],
|
||||
'1.1.2' =>
|
||||
[
|
||||
0 => 'itop-object-copier/1.1.2',
|
||||
],
|
||||
'1.1.3' =>
|
||||
[
|
||||
0 => 'itop-object-copier/1.1.3',
|
||||
],
|
||||
'1.1.4' =>
|
||||
[
|
||||
0 => 'itop-object-copier/1.1.4',
|
||||
],
|
||||
'1.1.5' =>
|
||||
[
|
||||
0 => 'itop-object-copier/1.1.5',
|
||||
],
|
||||
'1.1.6' =>
|
||||
[
|
||||
0 => 'itop-object-copier/1.1.6',
|
||||
],
|
||||
'1.1.7' =>
|
||||
[
|
||||
0 => 'itop-object-copier/1.1.7',
|
||||
],
|
||||
'1.1.8' =>
|
||||
[
|
||||
0 => 'itop-object-copier/1.1.8',
|
||||
],
|
||||
],
|
||||
],
|
||||
'combodo-send-updates-by-email' =>
|
||||
[
|
||||
'label' => 'Send updates by email',
|
||||
'description' => 'Send an email to pre-configured contacts when a ticket log is updated.',
|
||||
'versions' =>
|
||||
[
|
||||
'1.0.1' =>
|
||||
[
|
||||
0 => 'email-reply/1.0.1',
|
||||
],
|
||||
'1.0.3' =>
|
||||
[
|
||||
0 => 'email-reply/1.0.3',
|
||||
],
|
||||
'1.1.1' =>
|
||||
[
|
||||
0 => 'email-reply/1.1.1',
|
||||
],
|
||||
'1.1.2' =>
|
||||
[
|
||||
0 => 'email-reply/1.1.2',
|
||||
],
|
||||
'1.1.3' =>
|
||||
[
|
||||
0 => 'email-reply/1.1.3',
|
||||
],
|
||||
'1.1.4' =>
|
||||
[
|
||||
0 => 'email-reply/1.1.4',
|
||||
],
|
||||
'1.1.5' =>
|
||||
[
|
||||
0 => 'email-reply/1.1.5',
|
||||
],
|
||||
'1.1.6' =>
|
||||
[
|
||||
0 => 'email-reply/1.1.6',
|
||||
],
|
||||
'1.1.7' =>
|
||||
[
|
||||
0 => 'email-reply/1.1.7',
|
||||
],
|
||||
// 1.1.8 was never released
|
||||
'1.1.9' =>
|
||||
[
|
||||
0 => 'email-reply/1.1.9',
|
||||
],
|
||||
'1.1.10' =>
|
||||
[
|
||||
0 => 'email-reply/1.1.10',
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,6 +89,7 @@ class ExtensionInstallation extends cmdbAbstractObject
|
||||
MetaModel::Init_AddAttribute(new AttributeString("source", ["allowed_values" => null, "sql" => "source", "default_value" => null, "is_null_allowed" => false, "depends_on" => []]));
|
||||
MetaModel::Init_AddAttribute(new AttributeEnum("uninstallable", ["allowed_values" => new ValueSetEnum('yes,no,maybe'), "sql" => "uninstallable", "default_value" => 'yes', "is_null_allowed" => false, "depends_on" => []]));
|
||||
MetaModel::Init_AddAttribute(new AttributeDateTime("installed", ["allowed_values" => null, "sql" => "installed", "default_value" => 'NOW()', "is_null_allowed" => false, "depends_on" => []]));
|
||||
MetaModel::Init_AddAttribute(new AttributeText("description", ["allowed_values" => null, "sql" => "description", "default_value" => null, "is_null_allowed" => true, "depends_on" => []]));
|
||||
|
||||
// Display lists
|
||||
MetaModel::Init_SetZListItems('details', ['code', 'label', 'version', 'installed', 'source']); // Attributes to be displayed for the complete details
|
||||
|
||||
@@ -373,7 +373,7 @@ class RunTimeEnvironment
|
||||
// mark as (automatically) chosen alll the "remote" modules present in the
|
||||
// target environment (data/<target-env>-modules)
|
||||
// The actual choices will be recorded by RecordInstallation below
|
||||
$this->oExtensionsMap = new iTopExtensionsMap($this->sTargetEnv, true, $aExtraDirs);
|
||||
$this->oExtensionsMap = new iTopExtensionsMap($this->sTargetEnv, $aExtraDirs);
|
||||
$this->oExtensionsMap->LoadChoicesFromDatabase($oSourceConfig);
|
||||
foreach ($this->oExtensionsMap->GetAllExtensions() as $oExtension) {
|
||||
if ($this->IsExtensionSelected($oExtension)) {
|
||||
@@ -719,6 +719,7 @@ class RunTimeEnvironment
|
||||
$oInstallRec->Set('source', $oExtension->sSource);
|
||||
$oInstallRec->Set('uninstallable', $oExtension->CanBeUninstalled() ? 'yes' : 'no');
|
||||
$oInstallRec->Set('installed', $iInstallationTime);
|
||||
$oInstallRec->Set('description', $oExtension->sDescription);
|
||||
$oInstallRec->DBInsertNoReload();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,7 +79,7 @@ class InstallationFileService
|
||||
public function GetItopExtensionsMap(): ItopExtensionsMap
|
||||
{
|
||||
if (is_null($this->oItopExtensionsMap)) {
|
||||
$this->oItopExtensionsMap = new iTopExtensionsMap($this->sTargetEnvironment, true);
|
||||
$this->oItopExtensionsMap = new iTopExtensionsMap($this->sTargetEnvironment);
|
||||
}
|
||||
return $this->oItopExtensionsMap;
|
||||
}
|
||||
|
||||
@@ -210,12 +210,12 @@ HTML;
|
||||
}
|
||||
}
|
||||
$oPage->LinkScriptFromAppRoot('setup/setup.js');
|
||||
$oPage->add_script("function CanMoveForward()\n{\n".$oStep->JSCanMoveForward()."\n}\n");
|
||||
$oPage->add_script("function CanMoveBackward()\n{\n".$oStep->JSCanMoveBackward()."\n}\n");
|
||||
$oPage->add('<form id="wiz_form" class="ibo-setup--wizard" method="post">');
|
||||
$oPage->add('<div class="ibo-setup--wizard--content">');
|
||||
$oStep->Display($oPage);
|
||||
$oPage->add('</div>');
|
||||
$oPage->add_script("function CanMoveForward()\n{\n".$oStep->JSCanMoveForward()."\n}\n");
|
||||
$oPage->add_script("function CanMoveBackward()\n{\n".$oStep->JSCanMoveBackward()."\n}\n");
|
||||
|
||||
// Add the back / next buttons and the hidden form
|
||||
// to store the parameters
|
||||
|
||||
@@ -1310,14 +1310,16 @@ EOF
|
||||
*/
|
||||
class WizStepModulesChoice extends WizardStep
|
||||
{
|
||||
protected static $SEP = '_';
|
||||
protected $bUpgrade = false;
|
||||
protected static string $SEP = '_';
|
||||
protected bool $bUpgrade = false;
|
||||
protected bool $bCanMoveForward = true;
|
||||
protected ?Config $oConfig = null;
|
||||
|
||||
/**
|
||||
*
|
||||
* @var iTopExtensionsMap
|
||||
*/
|
||||
protected $oExtensionsMap;
|
||||
protected iTopExtensionsMap $oExtensionsMap;
|
||||
|
||||
protected PhpExpressionEvaluator $oPhpExpressionEvaluator;
|
||||
|
||||
@@ -1325,7 +1327,7 @@ class WizStepModulesChoice extends WizardStep
|
||||
* Whether we were able to load the choices from the database or not
|
||||
* @var bool
|
||||
*/
|
||||
protected $bChoicesFromDatabase;
|
||||
protected bool $bChoicesFromDatabase;
|
||||
|
||||
public function __construct(WizardController $oWizard, $sCurrentState)
|
||||
{
|
||||
@@ -1343,12 +1345,13 @@ class WizStepModulesChoice extends WizardStep
|
||||
// only called if the config file exists : we are updating a previous installation !
|
||||
// WARNING : we can't load this config directly, as it might be from another directory with a different approot_url (N°2684)
|
||||
if ($sConfigPath !== null) {
|
||||
$oConfig = new Config($sConfigPath);
|
||||
$this->oConfig = new Config($sConfigPath);
|
||||
|
||||
$aParamValues = $oWizard->GetParamForConfigArray();
|
||||
$oConfig->UpdateFromParams($aParamValues);
|
||||
$this->oConfig->UpdateFromParams($aParamValues);
|
||||
|
||||
$this->bChoicesFromDatabase = $this->oExtensionsMap->LoadChoicesFromDatabase($oConfig);
|
||||
$this->oExtensionsMap->LoadChoicesFromDatabase($this->oConfig);
|
||||
$this->bChoicesFromDatabase = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1452,7 +1455,7 @@ class WizStepModulesChoice extends WizardStep
|
||||
}
|
||||
$oPage->add('<img src="'.$sBannerUrl.'"/>');
|
||||
}
|
||||
$sDescription = isset($aStepInfo['description']) ? $aStepInfo['description'] : '';
|
||||
$sDescription = $aStepInfo['description'] ?? '';
|
||||
$oPage->add('<span>'.$sDescription.'</span>');
|
||||
$oPage->add('</div>');
|
||||
|
||||
@@ -1846,6 +1849,7 @@ EOF
|
||||
}
|
||||
return $index;
|
||||
}
|
||||
|
||||
protected function GetStepInfo($idx = null)
|
||||
{
|
||||
$aStepInfo = null;
|
||||
@@ -1866,12 +1870,12 @@ EOF
|
||||
// Additional step for the "extensions"
|
||||
$aStepDefinition = [
|
||||
'title' => 'Extensions',
|
||||
'description' => '<h2>Select additional extensions to install. You can launch the installation again to install new extensions, but you cannot remove already installed extensions.</h2>',
|
||||
'description' => '<h2>Select additional extensions to install. You can launch the installation again to install new extensions or remove installed ones.</h2>',
|
||||
'banner' => '/images/icons/icons8-puzzle.svg',
|
||||
'options' => [],
|
||||
];
|
||||
|
||||
foreach ($this->oExtensionsMap->GetAllExtensions() as $oExtension) {
|
||||
foreach ($this->oExtensionsMap->GetAllExtensionsWithPreviouslyInstalled() as $oExtension) {
|
||||
if (($oExtension->sSource !== iTopExtension::SOURCE_WIZARD) && ($oExtension->bVisible) && (count($oExtension->aMissingDependencies) == 0)) {
|
||||
$aStepDefinition['options'][] = [
|
||||
'extension_code' => $oExtension->sCode,
|
||||
@@ -1882,16 +1886,19 @@ EOF
|
||||
'modules' => $oExtension->aModules,
|
||||
'mandatory' => $oExtension->bMandatory || ($oExtension->sSource === iTopExtension::SOURCE_REMOTE),
|
||||
'source_label' => $this->GetExtensionSourceLabel($oExtension->sSource),
|
||||
'uninstallable' => $oExtension->CanBeUninstalled(),
|
||||
'missing' => $oExtension->bRemovedFromDisk,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
// Display this step of the wizard only if there is something to display
|
||||
if (count($aStepDefinition['options']) !== 0) {
|
||||
$aSteps[] = $aStepDefinition;
|
||||
$this->oWizard->SetParameter('additional_extensions_modules', json_encode($aStepDefinition['options']));
|
||||
}
|
||||
} else {
|
||||
// No wizard configuration provided, build a standard one with just one big list
|
||||
// No wizard configuration provided, build a standard one with just one big list. All items are mandatory, only works when there are no conflicted modules.
|
||||
$aStepDefinition = [
|
||||
'title' => 'Modules Selection',
|
||||
'description' => '<h2>Select the modules to install. You can launch the installation again to install new modules, but you cannot remove already installed modules.</h2>',
|
||||
@@ -1958,18 +1965,41 @@ EOF
|
||||
$sId = utils::EscapeHtml($aChoice['extension_code']);
|
||||
$bIsDefault = array_key_exists($sChoiceId, $aDefaults);
|
||||
|
||||
$bCanBeUninstalled = $this->oExtensionsMap->Get($aChoice['extension_code'])->CanBeUninstalled();
|
||||
$oITopExtension = $this->oExtensionsMap->GetFromExtensionCode($aChoice['extension_code']);
|
||||
$bCanBeUninstalled = isset($aChoice['uninstallable']) ? $aChoice['uninstallable'] : $oITopExtension->CanBeUninstalled();
|
||||
$bSelected = isset($aSelectedComponents[$sChoiceId]) && ($aSelectedComponents[$sChoiceId] == $sChoiceId);
|
||||
$bMandatory = (isset($aChoice['mandatory']) && $aChoice['mandatory']) || $this->bUpgrade && $bIsDefault && !$bCanBeUninstalled && !$bDisableUninstallCheck;
|
||||
;
|
||||
$bDisabled = $bMandatory || $bAllDisabled;
|
||||
$bMissingFromDisk = isset($aChoice['missing']) && $aChoice['missing'] === true;
|
||||
$bInstalled = $bMissingFromDisk || $oITopExtension->bInstalled;
|
||||
$bDisabled = $bMandatory || $bAllDisabled || $bMissingFromDisk;
|
||||
$bChecked = $bMandatory || $bSelected;
|
||||
|
||||
$sTooltip = '';
|
||||
$sUnremovable = '';
|
||||
if ($bMissingFromDisk) {
|
||||
$sTooltip .= '<span class="setup-extension-tag removed">source removed</span>';
|
||||
}
|
||||
if ($bInstalled) {
|
||||
$sTooltip .= '<span class="setup-extension-tag checked installed">installed</span>';
|
||||
$sTooltip .= '<span class="setup-extension-tag unchecked tobeuninstalled">to be uninstalled</span>';
|
||||
} else {
|
||||
$sTooltip .= '<span class="setup-extension-tag checked tobeinstalled">to be installed</span>';
|
||||
$sTooltip .= '<span class="setup-extension-tag unchecked notinstalled">not installed</span>';
|
||||
}
|
||||
if (!$bCanBeUninstalled) {
|
||||
$sTooltip .= '<span class="setup-extension-tag notuninstallable">cannot be uninstalled</span>';
|
||||
}
|
||||
if ($bDisabled && !$bChecked && !$bCanBeUninstalled && !$bDisableUninstallCheck) {
|
||||
$this->bCanMoveForward = false;//Disable "Next"
|
||||
}
|
||||
$sChecked = $bChecked ? ' checked ' : '';
|
||||
$sDisabled = $bDisabled ? ' disabled data-disabled="disabled" ' : '';
|
||||
$sUnremovable = !$bCanBeUninstalled ? ' unremovable ' : '';
|
||||
$sMissingModule = $bMissingFromDisk ? 'setup-extension--missing' : '';
|
||||
|
||||
$sHiddenInput = $bDisabled && $bChecked ? '<input type="hidden" name="choice['.$sChoiceId.']" value="'.$sChoiceId.'"/>' : '';
|
||||
$oPage->add('<div class="choice" '.$sDataId.'><input class="wiz-choice '.$sUnremovable.'" id="'.$sId.'" name="choice['.$sChoiceId.']" type="checkbox" value="'.$sChoiceId.'" '.$sDisabled.$sChecked.'/>'.$sHiddenInput.' ');
|
||||
$this->DisplayChoice($oPage, $aChoice, $aSelectedComponents, $aDefaults, $sChoiceId, $bDisabled, $bCanBeUninstalled);
|
||||
$oPage->add('<div class="choice '.$sMissingModule.'" '.$sDataId.'><input class="wiz-choice '.$sUnremovable.'" id="'.$sId.'" name="choice['.$sChoiceId.']" type="checkbox" value="'.$sChoiceId.'" '.$sDisabled.$sChecked.'/>'.$sHiddenInput.' ');
|
||||
$this->DisplayChoice($oPage, $aChoice, $aSelectedComponents, $aDefaults, $sChoiceId, $bDisabled, $sTooltip);
|
||||
$oPage->add('</div>');
|
||||
}
|
||||
$sChoiceName = null;
|
||||
@@ -2030,14 +2060,13 @@ EOF
|
||||
}
|
||||
}
|
||||
|
||||
protected function DisplayChoice($oPage, $aChoice, $aSelectedComponents, $aDefaults, $sChoiceId, $bDisabled = false, $bUninstallable = true)
|
||||
protected function DisplayChoice($oPage, $aChoice, $aSelectedComponents, $aDefaults, $sChoiceId, $bDisabled = false, $sTooltip = '')
|
||||
{
|
||||
$sMoreInfo = (isset($aChoice['more_info']) && ($aChoice['more_info'] != '')) ? '<a class="setup--wizard-choice--more-info" target="_blank" href="'.$aChoice['more_info'].'">More information</a>' : '';
|
||||
$sSourceLabel = isset($aChoice['source_label']) ? $aChoice['source_label'] : '';
|
||||
$sSourceLabel = $aChoice['source_label'] ?? '';
|
||||
$sId = utils::EscapeHtml($aChoice['extension_code']);
|
||||
$sUninstallationWarning = $bUninstallable ? '' : '<span style="color:orangered" title="Once this extension has been installed, it cannot be removed">(!)</span>';
|
||||
|
||||
$oPage->add('<label class="setup--wizard-choice--label" for="'.$sId.'">'.$sSourceLabel.'<b>'.utils::EscapeHtml($aChoice['title']).'</b>'.'</label> '.$sUninstallationWarning.' '.$sMoreInfo.'');
|
||||
$oPage->add('<label class="setup--wizard-choice--label" for="'.$sId.'">'.$sSourceLabel.'<b>'.utils::EscapeHtml($aChoice['title']).'</b>'.' '.$sTooltip.'</label> '.$sMoreInfo.'');
|
||||
$sDescription = isset($aChoice['description']) ? utils::EscapeHtml($aChoice['description']) : '';
|
||||
$oPage->add('<div class="setup--wizard-choice--description description">'.$sDescription.'<span id="sub_choices'.$sId.'">');
|
||||
if (isset($aChoice['sub_options'])) {
|
||||
@@ -2052,6 +2081,22 @@ EOF
|
||||
return $sSourceDir.'/installation.xml';
|
||||
}
|
||||
|
||||
public function CanMoveForward()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function JSCanMoveForward()
|
||||
{
|
||||
|
||||
return $this->bCanMoveForward ? 'return true;' : 'return false;';
|
||||
}
|
||||
|
||||
public function GetNextButtonLabel()
|
||||
{
|
||||
return $this->bCanMoveForward ? 'Next' : 'Non-uninstallable extension missing';
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user