mirror of
https://github.com/Combodo/iTop.git
synced 2026-05-20 15:52:24 +02:00
N°9567 - Extension Mgmt : Run setup (#912)
* N°9144 - correct next button in audit page * N°9567 - WIP code style * N°9412 - Screen Analysis results wip + test endpoint * N°9412 - next button label * N°9412 - Mask CI pbs * N°9412 - Analysis results screen wip * N°9412 - Analysis results screen wip * N°9567 - fix extension map init of installation choices * N°9567 - fix test * N°9567 - link from ext mgt to setup WIP * N°9567 - add enc_type in UIForm to be able to change content type in twigs * N°9412 - wip * N°9567 - Extension Mgmt : Run setup * N°9567 - Extension Mgmt : Run setup * N°9567 - Extension Mgmt : Run setup (fix post-deletion button inputs) * N°9567 - Extension Mgmt : Run setup * N°9567 - Extension Mgmt : Run setup * N°9567 - Extension Mgmt : Run setup --------- Co-authored-by: Eric Espie <eric.espie@combodo.com>
This commit is contained in:
@@ -21,6 +21,7 @@ Dict::Add('EN US', 'English', 'English', [
|
|||||||
'DataFeatureRemoval:Helper:Desc2' => 'Analyze if there are any data or dependency preventing you from enabling/disabling a feature.',
|
'DataFeatureRemoval:Helper:Desc2' => 'Analyze if there are any data or dependency preventing you from enabling/disabling a feature.',
|
||||||
|
|
||||||
'DataFeatureRemoval:Features:Title' => 'Features',
|
'DataFeatureRemoval:Features:Title' => 'Features',
|
||||||
|
'DataFeatureRemoval:Execution:Title' => 'Deletion Executions',
|
||||||
'DataFeatureRemoval:Analysis:Title' => 'Analysis result',
|
'DataFeatureRemoval:Analysis:Title' => 'Analysis result',
|
||||||
'DataFeatureRemoval:Analysis:SubTitle' => '%1$s element(s) to clean before continuing',
|
'DataFeatureRemoval:Analysis:SubTitle' => '%1$s element(s) to clean before continuing',
|
||||||
|
|
||||||
@@ -35,6 +36,9 @@ Dict::Add('EN US', 'English', 'English', [
|
|||||||
'DataFeatureRemoval:Table:Analysis:Module' => 'Module name',
|
'DataFeatureRemoval:Table:Analysis:Module' => 'Module name',
|
||||||
'DataFeatureRemoval:Table:Analysis:Occurrence' => 'Occurrence',
|
'DataFeatureRemoval:Table:Analysis:Occurrence' => 'Occurrence',
|
||||||
|
|
||||||
|
'DataFeatureRemoval:CleanupComplete:Title' => 'All clear.',
|
||||||
|
'DataFeatureRemoval:CompilComplete' => 'Compilation successful. No Cleanup needed. You can proceed to setup.',
|
||||||
|
|
||||||
'UI:Button:Analyze' => 'Analyze',
|
'UI:Button:Analyze' => 'Analyze',
|
||||||
'UI:Button:ModifyChoices' => 'Modify Choices',
|
'UI:Button:ModifyChoices' => 'Modify Choices',
|
||||||
'UI:Button:AnalyzeAndSetup' => 'Analyze and go to setup',
|
'UI:Button:AnalyzeAndSetup' => 'Analyze and go to setup',
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ Dict::Add('FR FR', 'French', 'Français', [
|
|||||||
'DataFeatureRemoval:Helper:Desc2' => 'Analyse si des données ou des dépendances empêchent l’activation ou la désactivation d’une fonctionnalité.',
|
'DataFeatureRemoval:Helper:Desc2' => 'Analyse si des données ou des dépendances empêchent l’activation ou la désactivation d’une fonctionnalité.',
|
||||||
|
|
||||||
'DataFeatureRemoval:Features:Title' => 'Fonctionnalités',
|
'DataFeatureRemoval:Features:Title' => 'Fonctionnalités',
|
||||||
|
'DataFeatureRemoval:Execution:Title' => 'Suppressions',
|
||||||
'DataFeatureRemoval:Analysis:Title' => 'Résultat de l’analyse',
|
'DataFeatureRemoval:Analysis:Title' => 'Résultat de l’analyse',
|
||||||
'DataFeatureRemoval:Analysis:SubTitle' => '%1$s élément(s) à nettoyer avant de poursuivre',
|
'DataFeatureRemoval:Analysis:SubTitle' => '%1$s élément(s) à nettoyer avant de poursuivre',
|
||||||
|
|
||||||
@@ -35,6 +36,9 @@ Dict::Add('FR FR', 'French', 'Français', [
|
|||||||
'DataFeatureRemoval:Table:Analysis:Module' => 'Module',
|
'DataFeatureRemoval:Table:Analysis:Module' => 'Module',
|
||||||
'DataFeatureRemoval:Table:Analysis:Occurrence' => 'Occurrence',
|
'DataFeatureRemoval:Table:Analysis:Occurrence' => 'Occurrence',
|
||||||
|
|
||||||
|
'DataFeatureRemoval:CleanupComplete:Title' => 'All clear.',
|
||||||
|
'DataFeatureRemoval:CompilComplete' => 'Compilation successful. No Cleanup needed. You can proceed to setup.',
|
||||||
|
|
||||||
'UI:Button:Analyze' => 'Analyser',
|
'UI:Button:Analyze' => 'Analyser',
|
||||||
'UI:Button:ModifyChoices' => 'Modifier les choix',
|
'UI:Button:ModifyChoices' => 'Modifier les choix',
|
||||||
'UI:Button:AnalyzeAndSetup' => 'Analyser et ouvrir l’assistant de configuration',
|
'UI:Button:AnalyzeAndSetup' => 'Analyser et ouvrir l’assistant de configuration',
|
||||||
|
|||||||
@@ -10,13 +10,17 @@ namespace Combodo\iTop\DataFeatureRemoval\Controller;
|
|||||||
require_once APPROOT.'setup/feature_removal/SetupAudit.php';
|
require_once APPROOT.'setup/feature_removal/SetupAudit.php';
|
||||||
require_once APPROOT.'setup/feature_removal/DryRemovalRuntimeEnvironment.php';
|
require_once APPROOT.'setup/feature_removal/DryRemovalRuntimeEnvironment.php';
|
||||||
|
|
||||||
|
use Combodo\iTop\Application\Helper\Session;
|
||||||
use Combodo\iTop\Application\TwigBase\Controller\Controller;
|
use Combodo\iTop\Application\TwigBase\Controller\Controller;
|
||||||
|
use Combodo\iTop\DataFeatureRemoval\Entity\DataCleanupSummaryEntity;
|
||||||
use Combodo\iTop\DataFeatureRemoval\Helper\DataFeatureRemovalException;
|
use Combodo\iTop\DataFeatureRemoval\Helper\DataFeatureRemovalException;
|
||||||
use Combodo\iTop\DataFeatureRemoval\Helper\DataFeatureRemovalHelper;
|
use Combodo\iTop\DataFeatureRemoval\Helper\DataFeatureRemovalHelper;
|
||||||
|
use Combodo\iTop\DataFeatureRemoval\Helper\DataFeatureRemovalLog;
|
||||||
use Combodo\iTop\DataFeatureRemoval\Service\DataCleanupService;
|
use Combodo\iTop\DataFeatureRemoval\Service\DataCleanupService;
|
||||||
use Combodo\iTop\DataFeatureRemoval\Service\DataFeatureRemoverExtensionService;
|
use Combodo\iTop\DataFeatureRemoval\Service\DataFeatureRemoverExtensionService;
|
||||||
use Combodo\iTop\Setup\FeatureRemoval\DryRemovalRuntimeEnvironment;
|
use Combodo\iTop\Setup\FeatureRemoval\DryRemovalRuntimeEnvironment;
|
||||||
use Combodo\iTop\Setup\FeatureRemoval\SetupAudit;
|
use Combodo\iTop\Setup\FeatureRemoval\SetupAudit;
|
||||||
|
use ContextTag;
|
||||||
use Dict;
|
use Dict;
|
||||||
use Exception;
|
use Exception;
|
||||||
use IssueLog;
|
use IssueLog;
|
||||||
@@ -26,9 +30,11 @@ use utils;
|
|||||||
|
|
||||||
class DataFeatureRemovalController extends Controller
|
class DataFeatureRemovalController extends Controller
|
||||||
{
|
{
|
||||||
private array $aSelectedExtensionsForCheck = [];
|
private array $aRemovedExtensionsForCheck = [];
|
||||||
private array $aCountClassesToCleanup = [];
|
private array $aCountClassesToCleanup = [];
|
||||||
private array $aAnalysisDataTable = [];
|
private array $aAnalysisDataTable = [];
|
||||||
|
private array $aDeletionExecutionSummary = [];
|
||||||
|
|
||||||
private int $iCount = 0;
|
private int $iCount = 0;
|
||||||
|
|
||||||
public function OperationMain($sErrorMessage = null): void
|
public function OperationMain($sErrorMessage = null): void
|
||||||
@@ -75,7 +81,7 @@ class DataFeatureRemovalController extends Controller
|
|||||||
$this->m_sOperation = 'Main';
|
$this->m_sOperation = 'Main';
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (count($this->aSelectedExtensionsForCheck) > 0) {
|
if (count($this->aRemovedExtensionsForCheck) > 0) {
|
||||||
$this->Analyze();
|
$this->Analyze();
|
||||||
}
|
}
|
||||||
$this->OperationMain();
|
$this->OperationMain();
|
||||||
@@ -87,10 +93,8 @@ class DataFeatureRemovalController extends Controller
|
|||||||
|
|
||||||
private function Analyze(): void
|
private function Analyze(): void
|
||||||
{
|
{
|
||||||
|
$this->Compile($this->aRemovedExtensionsForCheck);
|
||||||
$sSourceEnv = MetaModel::GetEnvironment();
|
$sSourceEnv = MetaModel::GetEnvironment();
|
||||||
$oDryRemovalRuntimeEnvironment = new DryRemovalRuntimeEnvironment($sSourceEnv, $this->aSelectedExtensionsForCheck);
|
|
||||||
$oDryRemovalRuntimeEnvironment->CompileFrom($sSourceEnv);
|
|
||||||
|
|
||||||
$oSetupAudit = new SetupAudit($sSourceEnv);
|
$oSetupAudit = new SetupAudit($sSourceEnv);
|
||||||
$aGetRemovedClasses = $oSetupAudit->RunDataAudit();
|
$aGetRemovedClasses = $oSetupAudit->RunDataAudit();
|
||||||
IssueLog::Debug(__METHOD__, null, ['aGetRemovedClasses' => $aGetRemovedClasses]);
|
IssueLog::Debug(__METHOD__, null, ['aGetRemovedClasses' => $aGetRemovedClasses]);
|
||||||
@@ -110,56 +114,112 @@ class DataFeatureRemovalController extends Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Display changed extensions
|
// Display changed extensions
|
||||||
$aAddedExtensions = utils::ReadPostedParam('aAddedExtensions', []);
|
$aHiddenInputNames = [
|
||||||
$aRemovedExtensions = utils::ReadPostedParam('aRemovedExtensions', []);
|
'selected_modules',
|
||||||
|
'selected_extensions',
|
||||||
|
'display_choices',
|
||||||
|
'added_extensions',
|
||||||
|
'removed_extensions',
|
||||||
|
'extensions_not_uninstallable',
|
||||||
|
];
|
||||||
|
|
||||||
IssueLog::Info(__METHOD__.' Extensions given in parameter', null, ['aAddedExtensions' => $aAddedExtensions, 'aRemovedExtensions' => $aRemovedExtensions]);
|
$aHiddenInputs = [];
|
||||||
|
foreach ($aHiddenInputNames as $sInputName) {
|
||||||
|
$aHiddenInputs[$sInputName] = utils::ReadPostedParam($sInputName, "[]", utils::ENUM_SANITIZATION_FILTER_RAW_DATA);
|
||||||
|
}
|
||||||
|
$aParams['aHiddenInputs'] = $aHiddenInputs;
|
||||||
|
|
||||||
|
$aAddedExtensions = json_decode($aHiddenInputs['added_extensions'], true);
|
||||||
|
$aRemovedExtensions = json_decode($aHiddenInputs['removed_extensions'], true);
|
||||||
|
|
||||||
|
$aParams['aAddedExtensions'] = $aAddedExtensions;
|
||||||
|
$aParams['aRemovedExtensions'] = $aRemovedExtensions;
|
||||||
|
|
||||||
|
IssueLog::Debug(__METHOD__.' Extensions given in parameter', null, [
|
||||||
|
'added_extensions' => $aAddedExtensions,
|
||||||
|
'removed_extensions' => $aRemovedExtensions]);
|
||||||
|
|
||||||
|
$this->Compile(array_keys($aRemovedExtensions), false);
|
||||||
|
|
||||||
$sSourceEnv = MetaModel::GetEnvironment();
|
$sSourceEnv = MetaModel::GetEnvironment();
|
||||||
$oSetupAudit = new SetupAudit($sSourceEnv);
|
$oSetupAudit = new SetupAudit($sSourceEnv);
|
||||||
$aGetRemovedClasses = array_keys($oSetupAudit->RunDataAudit());
|
$aGetRemovedClasses = array_keys($oSetupAudit->RunDataAudit());
|
||||||
IssueLog::Debug(__METHOD__, null, ['aGetRemovedClasses' => $aGetRemovedClasses]);
|
IssueLog::Debug(__METHOD__, null, ['aGetRemovedClasses' => $aGetRemovedClasses]);
|
||||||
|
|
||||||
$oDataCleanupService = new DataCleanupService();
|
|
||||||
$aDeletionPlanSummaryEntities = $oDataCleanupService->GetCleanupSummary($aGetRemovedClasses);
|
|
||||||
$aColumns = ['Class', 'DeleteCount' , 'UpdateCount', 'IssueCount'];
|
|
||||||
$aRows = [];
|
|
||||||
$iQueryCount = 0;
|
|
||||||
$bHasIssues = false;
|
|
||||||
foreach ($aDeletionPlanSummaryEntities as $oDeletionPlanSummaryEntity) {
|
|
||||||
$aRows[] = [
|
|
||||||
$oDeletionPlanSummaryEntity->sClass,
|
|
||||||
$oDeletionPlanSummaryEntity->iDeleteCount,
|
|
||||||
$oDeletionPlanSummaryEntity->iUpdateCount,
|
|
||||||
$oDeletionPlanSummaryEntity->iIssueCount,
|
|
||||||
];
|
|
||||||
$bHasIssues |= ($oDeletionPlanSummaryEntity->iIssueCount !== 0);
|
|
||||||
$iQueryCount += $oDeletionPlanSummaryEntity->iDeleteCount;
|
|
||||||
$iQueryCount += $oDeletionPlanSummaryEntity->iUpdateCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
$aParams['sTransactionId'] = utils::GetNewTransactionId();
|
$aParams['sTransactionId'] = utils::GetNewTransactionId();
|
||||||
$aParams['aDeletionPlanSummary'] = $this->GetTableData('Extensions', $aColumns, $aRows);
|
|
||||||
$aParams['aClasses'] = $aGetRemovedClasses;
|
$aParams['aClasses'] = $aGetRemovedClasses;
|
||||||
$aParams['iQueryCount'] = $iQueryCount;
|
|
||||||
$aParams['bDeletionPossible'] = !$bHasIssues;
|
|
||||||
$aParams['aAddedExtensions'] = $aAddedExtensions;
|
|
||||||
$aParams['aRemovedExtensions'] = $aRemovedExtensions;
|
|
||||||
$aParams['aExtensions'] = $this->GetExtensionsTableDiff($aAddedExtensions, $aRemovedExtensions);
|
$aParams['aExtensions'] = $this->GetExtensionsTableDiff($aAddedExtensions, $aRemovedExtensions);
|
||||||
|
|
||||||
$this->DisplayPage($aParams);
|
new ContextTag(ContextTag::TAG_SETUP);
|
||||||
|
$aParams['sLaunchSetupUrl'] = utils::GetAbsoluteUrlAppRoot().'setup/wizard.php';
|
||||||
|
$aParams['aSetupParams'] = array_merge([
|
||||||
|
"_class" => "WizStepLandingBeforeAudit",
|
||||||
|
"_params[authent]" => SetupUtils::CreateSetupToken(),
|
||||||
|
"operation" => "next",
|
||||||
|
], $aHiddenInputs);
|
||||||
|
|
||||||
|
[$aParams['aDeletionPlanSummary'], $aParams['iQueryCount'], $aParams['bDeletionPossible']] = $this->GetDeletionPlanSummaryTable($aGetRemovedClasses);
|
||||||
|
[$aParams['aDeletionExecutionSummary'], $aParams['bHasDeletionExecution']] = $this->GetExecutionSummaryTable();
|
||||||
|
$aParams['bDeletionNeeded'] = ($aParams['iQueryCount'] > 0);
|
||||||
|
Session::Set('aDeletionExecutionSummary', serialize($this->aDeletionExecutionSummary));
|
||||||
|
|
||||||
|
$this->DisplayPage($aParams, 'AnalysisResult');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function OperationDeletionPlan(): void
|
private function Compile(array $aRemovedExtensions, bool $bForceCompilation = true): void
|
||||||
{
|
{
|
||||||
$aParams = [];
|
$sSourceEnv = MetaModel::GetEnvironment();
|
||||||
$this->ValidateTransactionId();
|
$sBuildDir = APPROOT."/env-$sSourceEnv-build";
|
||||||
|
if (! is_dir($sBuildDir)) {
|
||||||
|
SetupUtils::builddir($sBuildDir);
|
||||||
|
}
|
||||||
|
$bIsDirEmpty = count(scandir($sBuildDir)) === 2;
|
||||||
|
|
||||||
$aClasses = utils::ReadPostedParam('classes', null, utils::ENUM_SANITIZATION_FILTER_CLASS);
|
if ($bIsDirEmpty || $bForceCompilation) {
|
||||||
|
$oRuntimeEnvironment = new DryRemovalRuntimeEnvironment($sSourceEnv, $aRemovedExtensions);
|
||||||
|
DataFeatureRemovalLog::Debug(
|
||||||
|
__METHOD__,
|
||||||
|
null,
|
||||||
|
['sSourceEnv' => $sSourceEnv, 'sBuildDir' => $sBuildDir, 'bIsDirEmpty' => $bIsDirEmpty, glob("$sBuildDir/*")]
|
||||||
|
);
|
||||||
|
$oRuntimeEnvironment->CompileFrom($sSourceEnv);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function GetExecutionSummaryTable(): array
|
||||||
|
{
|
||||||
|
$sName = 'ExcutionSummary';
|
||||||
|
|
||||||
|
$aTableData = [];
|
||||||
|
if (count($this->aDeletionExecutionSummary) === 0) {
|
||||||
|
return [$aTableData, false];
|
||||||
|
}
|
||||||
|
|
||||||
|
$aColumns = ['Class', 'Total Deleted Count' , 'Total Updated Count', 'Deleted Count' , 'Updated Count'];
|
||||||
|
$aRows = [];
|
||||||
|
/** @var DataCleanupSummaryEntity $oSummary */
|
||||||
|
foreach ($this->aDeletionExecutionSummary as $sClass => $oSummary) {
|
||||||
|
$aRows[] = [
|
||||||
|
$sClass,
|
||||||
|
$oSummary->iTotalDeleteCount,
|
||||||
|
$oSummary->iTotalUpdateCount,
|
||||||
|
$oSummary->iDeleteCount,
|
||||||
|
$oSummary->iUpdateCount,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
$aTableData = $this->GetTableData($sName, $aColumns, $aRows);
|
||||||
|
|
||||||
|
return [$aTableData, true];
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private function GetDeletionPlanSummaryTable(array $aRemovedClasses): array
|
||||||
|
{
|
||||||
|
$sName = 'DeletionPlanSummary';
|
||||||
$oDataCleanupService = new DataCleanupService();
|
$oDataCleanupService = new DataCleanupService();
|
||||||
$aDeletionPlanSummaryEntities = $oDataCleanupService->GetCleanupSummary($aClasses);
|
$aDeletionPlanSummaryEntities = $oDataCleanupService->GetCleanupSummary($aRemovedClasses);
|
||||||
$aColumns = ['Class', 'DeleteCount' , 'UpdateCount', 'IssueCount'];
|
$aColumns = ['Class', 'Delete Count' , 'Update Count', 'Issue Count'];
|
||||||
$aRows = [];
|
$aRows = [];
|
||||||
$iQueryCount = 0;
|
$iQueryCount = 0;
|
||||||
$bHasIssues = false;
|
$bHasIssues = false;
|
||||||
@@ -174,39 +234,31 @@ class DataFeatureRemovalController extends Controller
|
|||||||
$iQueryCount += $oDeletionPlanSummaryEntity->iDeleteCount;
|
$iQueryCount += $oDeletionPlanSummaryEntity->iDeleteCount;
|
||||||
$iQueryCount += $oDeletionPlanSummaryEntity->iUpdateCount;
|
$iQueryCount += $oDeletionPlanSummaryEntity->iUpdateCount;
|
||||||
}
|
}
|
||||||
|
return [$this->GetTableData($sName, $aColumns, $aRows), $iQueryCount, !$bHasIssues];
|
||||||
$aParams['sTransactionId'] = utils::GetNewTransactionId();
|
|
||||||
$aParams['aDeletionPlanSummary'] = $this->GetTableData('Extensions', $aColumns, $aRows);
|
|
||||||
$aParams['aClasses'] = $aClasses;
|
|
||||||
$aParams['iQueryCount'] = $iQueryCount;
|
|
||||||
$aParams['bDeletionPossible'] = !$bHasIssues;
|
|
||||||
|
|
||||||
$this->DisplayPage($aParams);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function OperationDoDeletion(): void
|
public function OperationDoDeletion(): void
|
||||||
{
|
{
|
||||||
$aParams = [];
|
|
||||||
$this->ValidateTransactionId();
|
$this->ValidateTransactionId();
|
||||||
|
|
||||||
|
$this->aDeletionExecutionSummary = unserialize(Session::Get('aDeletionExecutionSummary'));
|
||||||
|
Session::Unset('aDeletionExecutionSummary');
|
||||||
$aClasses = utils::ReadPostedParam('classes', null, utils::ENUM_SANITIZATION_FILTER_CLASS);
|
$aClasses = utils::ReadPostedParam('classes', null, utils::ENUM_SANITIZATION_FILTER_CLASS);
|
||||||
|
|
||||||
$oDataCleanupService = new DataCleanupService();
|
$oDataCleanupService = new DataCleanupService();
|
||||||
$aDeletionExecutionSummary = $oDataCleanupService->ExecuteCleanup($aClasses);
|
$aDeletionExecutionSummary = $oDataCleanupService->ExecuteCleanup($aClasses);
|
||||||
$aColumns = ['Class', 'DeletedCount' , 'UpdatedCount'];
|
foreach ($aDeletionExecutionSummary as $sClass => $oExecutionSummary) {
|
||||||
$aRows = [];
|
if (!array_key_exists($sClass, $this->aDeletionExecutionSummary)) {
|
||||||
foreach ($aDeletionExecutionSummary as $oDeletionExecutionSummaryEntity) {
|
$this->aDeletionExecutionSummary[$sClass] = new DataCleanupSummaryEntity($sClass);
|
||||||
$aRows[] = [
|
}
|
||||||
$oDeletionExecutionSummaryEntity->sClass,
|
$oSummary = $this->aDeletionExecutionSummary[$sClass];
|
||||||
$oDeletionExecutionSummaryEntity->iDeleteCount,
|
$oSummary->iDeleteCount = $oExecutionSummary->iDeleteCount;
|
||||||
$oDeletionExecutionSummaryEntity->iUpdateCount,
|
$oSummary->iUpdateCount = $oExecutionSummary->iUpdateCount;
|
||||||
];
|
$oSummary->iTotalDeleteCount += $oExecutionSummary->iDeleteCount;
|
||||||
|
$oSummary->iTotalUpdateCount += $oExecutionSummary->iUpdateCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
$aParams['sTransactionId'] = utils::GetNewTransactionId();
|
$this->OperationAnalysisResult();
|
||||||
$aParams['aDeletionExecutionSummary'] = $this->GetTableData('Extensions', $aColumns, $aRows);
|
|
||||||
|
|
||||||
$this->DisplayPage($aParams);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private function GetExtensionsTableDiff(array $aAddedExtensions, array $aRemovedExtensions): array
|
private function GetExtensionsTableDiff(array $aAddedExtensions, array $aRemovedExtensions): array
|
||||||
@@ -256,7 +308,7 @@ HTML,
|
|||||||
if ($oExtension->bRemovedFromDisk) {
|
if ($oExtension->bRemovedFromDisk) {
|
||||||
$sDisabledHtml = 'disabled=""';
|
$sDisabledHtml = 'disabled=""';
|
||||||
$sChecked = 'checked';
|
$sChecked = 'checked';
|
||||||
} elseif (in_array($sCode, $this->aSelectedExtensionsForCheck)) {
|
} elseif (in_array($sCode, $this->aRemovedExtensionsForCheck)) {
|
||||||
$sChecked = 'checked';
|
$sChecked = 'checked';
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -322,7 +374,7 @@ HTML,
|
|||||||
*/
|
*/
|
||||||
public function ReadRemovedExtensions(): void
|
public function ReadRemovedExtensions(): void
|
||||||
{
|
{
|
||||||
if (count($this->aSelectedExtensionsForCheck) > 0) {
|
if (count($this->aRemovedExtensionsForCheck) > 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -330,14 +382,14 @@ HTML,
|
|||||||
foreach ($aSelectedExtensionsFromUI as $sCode => $aData) {
|
foreach ($aSelectedExtensionsFromUI as $sCode => $aData) {
|
||||||
$sValue = $aData['enable'] ?? 'off';
|
$sValue = $aData['enable'] ?? 'off';
|
||||||
if (($sValue) === 'on') {
|
if (($sValue) === 'on') {
|
||||||
$this->aSelectedExtensionsForCheck[] = $sCode;
|
$this->aRemovedExtensionsForCheck[] = $sCode;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add source removed to check
|
// Add source removed to check
|
||||||
foreach (DataFeatureRemoverExtensionService::GetInstance()->ReadItopExtensions() as $sCode => $oExtension) {
|
foreach (DataFeatureRemoverExtensionService::GetInstance()->ReadItopExtensions() as $sCode => $oExtension) {
|
||||||
if ($oExtension->bRemovedFromDisk) {
|
if ($oExtension->bRemovedFromDisk) {
|
||||||
$this->aSelectedExtensionsForCheck[] = $sCode;
|
$this->aRemovedExtensionsForCheck[] = $sCode;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ class DataCleanupSummaryEntity
|
|||||||
public int $iIssueCount = 0;
|
public int $iIssueCount = 0;
|
||||||
public int $iUpdateCount = 0;
|
public int $iUpdateCount = 0;
|
||||||
public int $iDeleteCount = 0;
|
public int $iDeleteCount = 0;
|
||||||
|
public int $iTotalUpdateCount = 0;
|
||||||
|
public int $iTotalDeleteCount = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $sClass
|
* @param string $sClass
|
||||||
|
|||||||
@@ -23,27 +23,29 @@ class ObjectServiceSummary implements iObjectService
|
|||||||
public function Update(DBObject $oToUpdate, string $sAttCode, $value): void
|
public function Update(DBObject $oToUpdate, string $sAttCode, $value): void
|
||||||
{
|
{
|
||||||
$sClass = get_class($oToUpdate);
|
$sClass = get_class($oToUpdate);
|
||||||
DataFeatureRemovalLog::Info('Object to update', null, ['class' => $sClass, 'id' => $oToUpdate->GetKey(), 'code' => $sAttCode, 'value' => "$value"]);
|
DataFeatureRemovalLog::Debug('Object to update', null, ['class' => $sClass, 'id' => $oToUpdate->GetKey(), 'code' => $sAttCode, 'value' => "$value"]);
|
||||||
if (! array_key_exists($sClass, $this->aSummary)) {
|
if (! array_key_exists($sClass, $this->aSummary)) {
|
||||||
$this->aSummary[$sClass] = new DataCleanupSummaryEntity($sClass);
|
$this->aSummary[$sClass] = new DataCleanupSummaryEntity($sClass);
|
||||||
}
|
}
|
||||||
$oDeletionPlanSummaryEntity = $this->aSummary[$sClass];
|
$oDeletionPlanSummaryEntity = $this->aSummary[$sClass];
|
||||||
$oDeletionPlanSummaryEntity->iUpdateCount++;
|
$oDeletionPlanSummaryEntity->iUpdateCount++;
|
||||||
|
$oDeletionPlanSummaryEntity->iTotalUpdateCount++;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function Delete(string $sClass, string $sId): void
|
public function Delete(string $sClass, string $sId): void
|
||||||
{
|
{
|
||||||
DataFeatureRemovalLog::Info('Object to delete', null, ['class' => $sClass, 'id' => $sId]);
|
DataFeatureRemovalLog::Debug('Object to delete', null, ['class' => $sClass, 'id' => $sId]);
|
||||||
if (!array_key_exists($sClass, $this->aSummary)) {
|
if (!array_key_exists($sClass, $this->aSummary)) {
|
||||||
$this->aSummary[$sClass] = new DataCleanupSummaryEntity($sClass);
|
$this->aSummary[$sClass] = new DataCleanupSummaryEntity($sClass);
|
||||||
}
|
}
|
||||||
$oDeletionPlanSummaryEntity = $this->aSummary[$sClass];
|
$oDeletionPlanSummaryEntity = $this->aSummary[$sClass];
|
||||||
$oDeletionPlanSummaryEntity->iDeleteCount++;
|
$oDeletionPlanSummaryEntity->iDeleteCount++;
|
||||||
|
$oDeletionPlanSummaryEntity->iTotalDeleteCount++;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function SetIssue(string $sClass): void
|
public function SetIssue(string $sClass): void
|
||||||
{
|
{
|
||||||
DataFeatureRemovalLog::Info('Issue on object', null, ['class' => $sClass]);
|
DataFeatureRemovalLog::Debug('Issue on object', null, ['class' => $sClass]);
|
||||||
if (!array_key_exists($sClass, $this->aSummary)) {
|
if (!array_key_exists($sClass, $this->aSummary)) {
|
||||||
$this->aSummary[$sClass] = new DataCleanupSummaryEntity($sClass);
|
$this->aSummary[$sClass] = new DataCleanupSummaryEntity($sClass);
|
||||||
}
|
}
|
||||||
@@ -55,4 +57,15 @@ class ObjectServiceSummary implements iObjectService
|
|||||||
{
|
{
|
||||||
return $this->aSummary;
|
return $this->aSummary;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function SetSummary(array $aSummary): void
|
||||||
|
{
|
||||||
|
foreach ($aSummary as $sClass => $oPreviousSummaryEntity) {
|
||||||
|
$oSummaryEntity = new DataCleanupSummaryEntity($sClass);
|
||||||
|
$oSummaryEntity->iTotalUpdateCount = $oPreviousSummaryEntity->iTotalUpdateCount;
|
||||||
|
$oSummaryEntity->iTotalDeleteCount = $oPreviousSummaryEntity->iTotalDeleteCount;
|
||||||
|
|
||||||
|
$this->aSummary[$sClass] = $oSummaryEntity;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,23 +9,48 @@
|
|||||||
{% UIDataTable ForForm { sRef:'aExtensions', aColumns:aExtensions.Columns, aData:aExtensions.Data} %}{% EndUIDataTable %}
|
{% UIDataTable ForForm { sRef:'aExtensions', aColumns:aExtensions.Columns, aData:aExtensions.Data} %}{% EndUIDataTable %}
|
||||||
{% EndUIFieldSet %}
|
{% EndUIFieldSet %}
|
||||||
|
|
||||||
{% UIFieldSet Standard {sLegend:'DataFeatureRemoval:DeletionPlan:Title'|dict_s} %}
|
{% if bDeletionNeeded %}
|
||||||
{% UIDataTable ForForm { sRef:'aDeletionPlanSummary', aColumns:aDeletionPlanSummary.Columns, aData:aDeletionPlanSummary.Data} %}{% EndUIDataTable %}
|
{% UIFieldSet Standard {sLegend:'DataFeatureRemoval:DeletionPlan:Title'|dict_s} %}
|
||||||
{% EndUIFieldSet %}
|
{% UIDataTable ForForm { sRef:'aDeletionPlanSummary', aColumns:aDeletionPlanSummary.Columns, aData:aDeletionPlanSummary.Data} %}{% EndUIDataTable %}
|
||||||
|
{% EndUIFieldSet %}
|
||||||
{% if bDeletionPossible %}
|
{% if bDeletionPossible %}
|
||||||
{% UIForm Standard {} %}
|
{% UIForm Standard {} %}
|
||||||
{% UIInput ForHidden { sName:'transaction_id', sValue:sTransactionId} %}
|
{% UIInput ForHidden { sName:'transaction_id', sValue:sTransactionId} %}
|
||||||
{% UIInput ForHidden { sName:'operation', sValue:'DoDeletion'} %}
|
{% UIInput ForHidden { sName:'operation', sValue:'DoDeletion'} %}
|
||||||
{% for sKey, sClass in aClasses %}
|
{% for sKey, sClass in aClasses %}
|
||||||
{% UIInput ForHidden { sName:"classes[" ~ sKey ~ "]", sValue:sClass } %}
|
{% UIInput ForHidden { sName:"classes[" ~ sKey ~ "]", sValue:sClass } %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% UIToolbar ForButton {} %}
|
{% for sCode, sLabel in aAddedExtensions %}
|
||||||
{% UIButton ForPrimaryAction {sLabel:'UI:Button:DoDeletion'|dict_s, sName:'btn_deletion', sId:'btn_deletion', bIsSubmit:true} %}
|
{% UIInput ForHidden { sName:"aAddedExtensions[" ~ sCode ~ "]", sValue:sLabel } %}
|
||||||
{% EndUIToolbar %}
|
{% endfor %}
|
||||||
{% EndUIForm %}
|
{% for sCode, sLabel in aRemovedExtensions %}
|
||||||
|
{% UIInput ForHidden { sName:"aRemovedExtensions[" ~ sCode ~ "]", sValue:sLabel } %}
|
||||||
|
{% endfor %}
|
||||||
|
{% for sInputName, sValue in aHiddenInputs %}
|
||||||
|
{% UIInput ForHidden { sName:sInputName, sValue:sValue } %}
|
||||||
|
{% endfor %}
|
||||||
|
{% UIToolbar ForButton {} %}
|
||||||
|
{% UIButton ForPrimaryAction {sLabel:'UI:Button:DoDeletion'|dict_s, sName:'btn_deletion', sId:'btn_deletion', bIsSubmit:true} %}
|
||||||
|
{% EndUIToolbar %}
|
||||||
|
{% EndUIForm %}
|
||||||
|
{% else %}
|
||||||
|
{% UIAlert ForFailure { sContent: 'DataFeatureRemoval:DeletionPlan:Error:Issues'|dict_s } %}{% EndUIAlert %}
|
||||||
|
{% endif %}
|
||||||
{% else %}
|
{% else %}
|
||||||
{{ 'DataFeatureRemoval:DeletionPlan:Error:Issues'|dict_s }}
|
{% UIAlert ForSuccess { sTitle:'DataFeatureRemoval:CleanupComplete:Title'|dict_s, sContent:'DataFeatureRemoval:CompilComplete'|dict_s, sId:value } %}{% EndUIAlert %}
|
||||||
|
|
||||||
|
{% UIForm Standard {'sId':'launch-setup-form', Action:sLaunchSetupUrl, 'EncType': 'application/x-www-form-urlencoded'} %}
|
||||||
|
{% for sKey, sValue in aSetupParams %}
|
||||||
|
{% UIInput ForHidden { sName:sKey, sValue:sValue } %}
|
||||||
|
{% endfor %}
|
||||||
|
{% UIButton ForPrimaryAction {sLabel:'UI:Button:Setup'|dict_s, sName:'btn_setup', sId:'btn_setup', bIsSubmit:true} %}
|
||||||
|
{% EndUIForm %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if bHasDeletionExecution %}
|
||||||
|
{% UIFieldSet Standard {sLegend:'DataFeatureRemoval:Execution:Title'|dict_s} %}
|
||||||
|
{% UIDataTable ForForm { sRef:'aDeletionExecutionSummary', aColumns:aDeletionExecutionSummary.Columns, aData:aDeletionExecutionSummary.Data} %}{% EndUIDataTable %}
|
||||||
|
{% EndUIFieldSet %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% UIForm Standard {} %}
|
{% UIForm Standard {} %}
|
||||||
|
|||||||
@@ -43,48 +43,53 @@ class iTopExtensionsMap
|
|||||||
*
|
*
|
||||||
* @param string $sFromEnvironment The environment to scan
|
* @param string $sFromEnvironment The environment to scan
|
||||||
* @param array $aExtraDirs extensions dir to scan
|
* @param array $aExtraDirs extensions dir to scan
|
||||||
|
* @param array $aExtraDirs extensions dir to scan
|
||||||
|
* @param string|null $sAppRootForTests
|
||||||
*
|
*
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
public function __construct(string $sFromEnvironment = ITOP_DEFAULT_ENV, array $aExtraDirs = [])
|
public function __construct(string $sFromEnvironment = ITOP_DEFAULT_ENV, array $aExtraDirs = [], ?string $sAppRootForTests = null)
|
||||||
{
|
{
|
||||||
$this->aExtensions = [];
|
$this->aExtensions = [];
|
||||||
$this->aExtensionsByCode = [];
|
$this->aExtensionsByCode = [];
|
||||||
$this->aScannedDirs = [];
|
$this->aScannedDirs = [];
|
||||||
$this->ScanDisk($sFromEnvironment);
|
|
||||||
|
$sAppRoot = $sAppRootForTests ?? APPROOT;
|
||||||
|
$this->ScanDisk($sFromEnvironment, $sAppRoot);
|
||||||
|
|
||||||
$this->aExtraDirs = $aExtraDirs;
|
$this->aExtraDirs = $aExtraDirs;
|
||||||
if (is_dir(APPROOT.'extensions')) {
|
if (is_dir($sAppRoot.'extensions')) {
|
||||||
$this->aExtraDirs [] = APPROOT.'extensions';
|
$this->aExtraDirs [] = $sAppRoot.'extensions';
|
||||||
}
|
}
|
||||||
if (is_dir(APPROOT.'data/'.$sFromEnvironment.'-modules')) {
|
if (is_dir($sAppRoot.'data/'.$sFromEnvironment.'-modules')) {
|
||||||
$this->aExtraDirs [] = APPROOT.'data/'.$sFromEnvironment.'-modules';
|
$this->aExtraDirs [] = $sAppRoot.'data/'.$sFromEnvironment.'-modules';
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($aExtraDirs as $sDir) {
|
foreach ($aExtraDirs as $sDir) {
|
||||||
$this->ReadDir($sDir, iTopExtension::SOURCE_REMOTE);
|
$this->ReadDir($sDir, iTopExtension::SOURCE_REMOTE);
|
||||||
}
|
}
|
||||||
$this->CheckDependencies();
|
$this->CheckDependencies($sAppRoot);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Populate the list of available (pseudo)extensions by scanning the disk
|
* Populate the list of available (pseudo)extensions by scanning the disk
|
||||||
* where the iTop files are located
|
* where the iTop files are located
|
||||||
* @param string $sEnvironment
|
* @param string $sEnvironment
|
||||||
|
* @param string $sAppRoot
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
protected function ScanDisk($sEnvironment)
|
protected function ScanDisk($sEnvironment, string $sAppRoot)
|
||||||
{
|
{
|
||||||
if (!$this->ReadInstallationWizard(APPROOT.'/datamodels/2.x')) {
|
if (!$this->ReadInstallationWizard($sAppRoot.'/datamodels/2.x')) {
|
||||||
$this->bHasXmlInstallationFile = false;
|
$this->bHasXmlInstallationFile = false;
|
||||||
//no installation xml found in 2.x: let's read all extensions in 2.x first
|
//no installation xml found in 2.x: let's read all extensions in 2.x first
|
||||||
if (!$this->ReadDir(APPROOT.'datamodels/2.x', iTopExtension::SOURCE_WIZARD)) {
|
if (!$this->ReadDir($sAppRoot.'datamodels/2.x', iTopExtension::SOURCE_WIZARD)) {
|
||||||
//nothing found in 2.x : fallback read in 1.x (flat structure)
|
//nothing found in 2.x : fallback read in 1.x (flat structure)
|
||||||
$this->ReadDir(APPROOT.'datamodels/1.x', iTopExtension::SOURCE_WIZARD);
|
$this->ReadDir($sAppRoot.'datamodels/1.x', iTopExtension::SOURCE_WIZARD);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$this->ReadDir(APPROOT.'extensions', iTopExtension::SOURCE_MANUAL);
|
$this->ReadDir($sAppRoot.'extensions', iTopExtension::SOURCE_MANUAL);
|
||||||
$this->ReadDir(APPROOT.'data/'.$sEnvironment.'-modules', iTopExtension::SOURCE_REMOTE);
|
$this->ReadDir($sAppRoot.'data/'.$sEnvironment.'-modules', iTopExtension::SOURCE_REMOTE);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -99,24 +104,58 @@ class iTopExtensionsMap
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$aModuleConfigs = [];
|
||||||
|
$this->ListModuleFiles(basename($sDir), dirname($sDir), $aModuleConfigs);
|
||||||
|
|
||||||
$oXml = new XMLParameters($sDir.'/installation.xml');
|
$oXml = new XMLParameters($sDir.'/installation.xml');
|
||||||
foreach ($oXml->Get('steps') as $aStepInfo) {
|
foreach ($oXml->Get('steps') as $aStepInfo) {
|
||||||
if (array_key_exists('options', $aStepInfo)) {
|
if (array_key_exists('options', $aStepInfo)) {
|
||||||
$this->ProcessWizardChoices($aStepInfo['options']);
|
$this->ProcessWizardChoices($aStepInfo['options'], $aModuleConfigs);
|
||||||
}
|
}
|
||||||
if (array_key_exists('alternatives', $aStepInfo)) {
|
if (array_key_exists('alternatives', $aStepInfo)) {
|
||||||
$this->ProcessWizardChoices($aStepInfo['alternatives']);
|
$this->ProcessWizardChoices($aStepInfo['alternatives'], $aModuleConfigs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function ListModuleFiles(string $sRelDir, string $sRootDir, array &$aRes): void
|
||||||
|
{
|
||||||
|
$sDirectory = $sRootDir.'/'.$sRelDir;
|
||||||
|
|
||||||
|
if ($hDir = opendir($sDirectory)) {
|
||||||
|
// This is the correct way to loop over the directory. (according to the documentation)
|
||||||
|
while (($sFile = readdir($hDir)) !== false) {
|
||||||
|
$aMatches = [];
|
||||||
|
if (is_dir($sDirectory.'/'.$sFile)) {
|
||||||
|
if (($sFile != '.') && ($sFile != '..') && ($sFile != '.svn') && ($sFile != 'vendor')) {
|
||||||
|
$this->ListModuleFiles($sRelDir.'/'.$sFile, $sRootDir, $aRes);
|
||||||
|
}
|
||||||
|
} elseif (preg_match('/^module\.(.*).php$/i', $sFile, $aMatches)) {
|
||||||
|
try {
|
||||||
|
$aModuleInfo = ModuleFileReader::GetInstance()->ReadModuleFileInformation($sDirectory.'/'.$sFile);
|
||||||
|
$sModuleId = $aModuleInfo[ModuleFileReader::MODULE_INFO_ID];
|
||||||
|
list($sModuleName, $sModuleVersion) = ModuleDiscovery::GetModuleName($sModuleId);
|
||||||
|
$aModuleConfig = $aModuleInfo[ModuleFileReader::MODULE_INFO_CONFIG];
|
||||||
|
$aModuleConfig['module_version'] = $sModuleVersion;
|
||||||
|
$aRes[$sModuleName] = $aModuleConfig;
|
||||||
|
} catch (ModuleFileReaderException $e) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
closedir($hDir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper to process a "choice" array read from the installation.xml file
|
* Helper to process a "choice" array read from the installation.xml file
|
||||||
* @param array $aChoices
|
* @param array $aChoices
|
||||||
|
* @param array $aModuleConfigs
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
protected function ProcessWizardChoices($aChoices)
|
protected function ProcessWizardChoices($aChoices, $aModuleConfigs)
|
||||||
{
|
{
|
||||||
foreach ($aChoices as $aChoiceInfo) {
|
foreach ($aChoices as $aChoiceInfo) {
|
||||||
if (array_key_exists('extension_code', $aChoiceInfo)) {
|
if (array_key_exists('extension_code', $aChoiceInfo)) {
|
||||||
@@ -128,13 +167,23 @@ class iTopExtensionsMap
|
|||||||
if (array_key_exists('modules', $aChoiceInfo)) {
|
if (array_key_exists('modules', $aChoiceInfo)) {
|
||||||
// Some wizard choices are not associated with any module
|
// Some wizard choices are not associated with any module
|
||||||
$oExtension->aModules = $aChoiceInfo['modules'];
|
$oExtension->aModules = $aChoiceInfo['modules'];
|
||||||
|
foreach ($oExtension->aModules as $sModuleName) {
|
||||||
|
$aCurrentModuleConfig = $aModuleConfigs[$sModuleName] ?? null;
|
||||||
|
if (is_null($aCurrentModuleConfig)) {
|
||||||
|
IssueLog::Debug("Installation choice comes with missing module file", null, ["choice" => $oExtension->sCode, 'module' => $sModuleName]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$oExtension->aModuleVersion[$sModuleName] = $aCurrentModuleConfig['module_version'];
|
||||||
|
unset($aCurrentModuleConfig['module_version']);
|
||||||
|
$oExtension->aModuleInfo[$sModuleName] = $aCurrentModuleConfig;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (array_key_exists('sub_options', $aChoiceInfo)) {
|
if (array_key_exists('sub_options', $aChoiceInfo)) {
|
||||||
if (array_key_exists('options', $aChoiceInfo['sub_options'])) {
|
if (array_key_exists('options', $aChoiceInfo['sub_options'])) {
|
||||||
$this->ProcessWizardChoices($aChoiceInfo['sub_options']['options']);
|
$this->ProcessWizardChoices($aChoiceInfo['sub_options']['options'], $aModuleConfigs);
|
||||||
}
|
}
|
||||||
if (array_key_exists('alternatives', $aChoiceInfo['sub_options'])) {
|
if (array_key_exists('alternatives', $aChoiceInfo['sub_options'])) {
|
||||||
$this->ProcessWizardChoices($aChoiceInfo['sub_options']['alternatives']);
|
$this->ProcessWizardChoices($aChoiceInfo['sub_options']['alternatives'], $aModuleConfigs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$this->AddExtension($oExtension);
|
$this->AddExtension($oExtension);
|
||||||
@@ -207,7 +256,7 @@ class iTopExtensionsMap
|
|||||||
$oExtension = $this->GetFromExtensionCode($sCode);
|
$oExtension = $this->GetFromExtensionCode($sCode);
|
||||||
if (!is_null($oExtension)) {
|
if (!is_null($oExtension)) {
|
||||||
$aRemovedExtension [] = $oExtension;
|
$aRemovedExtension [] = $oExtension;
|
||||||
\IssueLog::Info(__METHOD__.": remove extension locally", null, ['extension_code' => $oExtension->sCode]);
|
\IssueLog::Debug(__METHOD__.": remove extension locally", null, ['extension_code' => $oExtension->sCode]);
|
||||||
} else {
|
} else {
|
||||||
\IssueLog::Warning(__METHOD__." cannot find extensions", null, ['code' => $sCode]);
|
\IssueLog::Warning(__METHOD__." cannot find extensions", null, ['code' => $sCode]);
|
||||||
}
|
}
|
||||||
@@ -334,19 +383,19 @@ class iTopExtensionsMap
|
|||||||
/**
|
/**
|
||||||
* Check if some extension contains a module with missing dependencies...
|
* Check if some extension contains a module with missing dependencies...
|
||||||
* If so, populate the aMissingDepenencies array
|
* If so, populate the aMissingDepenencies array
|
||||||
|
* @param string $sAppRoot
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
protected function CheckDependencies()
|
protected function CheckDependencies(string $sAppRoot)
|
||||||
{
|
{
|
||||||
$aSearchDirs = [];
|
$aSearchDirs = [];
|
||||||
|
|
||||||
if (is_dir(APPROOT.'/datamodels/2.x')) {
|
if (is_dir($sAppRoot.'/datamodels/2.x')) {
|
||||||
$aSearchDirs[] = APPROOT.'/datamodels/2.x';
|
$aSearchDirs[] = $sAppRoot.'/datamodels/2.x';
|
||||||
} elseif (is_dir(APPROOT.'/datamodels/1.x')) {
|
} elseif (is_dir($sAppRoot.'/datamodels/1.x')) {
|
||||||
$aSearchDirs[] = APPROOT.'/datamodels/1.x';
|
$aSearchDirs[] = $sAppRoot.'/datamodels/1.x';
|
||||||
}
|
}
|
||||||
$aSearchDirs = array_merge($aSearchDirs, $this->aScannedDirs);
|
$aSearchDirs = array_merge($aSearchDirs, $this->aScannedDirs);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
ModuleDiscovery::GetModulesOrderedByDependencies($aSearchDirs, true);
|
ModuleDiscovery::GetModulesOrderedByDependencies($aSearchDirs, true);
|
||||||
} catch (MissingDependencyException $e) {
|
} catch (MissingDependencyException $e) {
|
||||||
|
|||||||
@@ -107,7 +107,7 @@ abstract class AbstractSetupAudit
|
|||||||
if (ContextTag::Check(ContextTag::TAG_SETUP)) {
|
if (ContextTag::Check(ContextTag::TAG_SETUP)) {
|
||||||
SetupLog::Info($sMessage, $sChannel, $aContext);
|
SetupLog::Info($sMessage, $sChannel, $aContext);
|
||||||
} else {
|
} else {
|
||||||
IssueLog::Info($sMessage, $sChannel, $aContext);
|
IssueLog::Debug($sMessage, $sChannel, $aContext);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ use ContextTag;
|
|||||||
use CoreException;
|
use CoreException;
|
||||||
use Exception;
|
use Exception;
|
||||||
use IssueLog;
|
use IssueLog;
|
||||||
use MetaModel;
|
|
||||||
use SetupLog;
|
use SetupLog;
|
||||||
use utils;
|
use utils;
|
||||||
|
|
||||||
@@ -34,7 +33,7 @@ class ModelReflectionSerializer
|
|||||||
|
|
||||||
public function GetModelFromEnvironment(string $sEnv): array
|
public function GetModelFromEnvironment(string $sEnv): array
|
||||||
{
|
{
|
||||||
IssueLog::Info(__METHOD__, null, ['env' => $sEnv]);
|
IssueLog::Debug(__METHOD__, null, ['env' => $sEnv]);
|
||||||
|
|
||||||
$sPHPExec = trim(utils::GetConfig()->Get('php_path'));
|
$sPHPExec = trim(utils::GetConfig()->Get('php_path'));
|
||||||
$sOutput = "";
|
$sOutput = "";
|
||||||
|
|||||||
@@ -141,4 +141,32 @@ class iTopExtension
|
|||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function __serialize(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'sCode' => $this->sCode,
|
||||||
|
'sSource' => $this->sSource,
|
||||||
|
'sVersion' => $this->sVersion,
|
||||||
|
'aModules' => $this->aModules,
|
||||||
|
'aModuleVersion' => $this->aModuleVersion,
|
||||||
|
'aModuleInfo' => $this->aModuleInfo,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __unserialize(array $aData): void
|
||||||
|
{
|
||||||
|
$this->sCode = $aData['sCode'] ?? '';
|
||||||
|
$this->sSource = $aData['sSource'] ?? '';
|
||||||
|
$this->sVersion = $aData['sVersion'] ?? '';
|
||||||
|
$this->aModules = $aData['aModules'] ?? '';
|
||||||
|
$this->aModuleVersion = $aData['aModuleVersion'] ?? '';
|
||||||
|
$this->aModuleInfo = $aData['aModuleInfo'] ?? '';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __toString(): string
|
||||||
|
{
|
||||||
|
return json_encode($this->__serialize(), JSON_PRETTY_PRINT);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -355,9 +355,11 @@ class ModuleDiscovery
|
|||||||
*/
|
*/
|
||||||
protected static function ListModuleFiles($sRelDir, $sRootDir)
|
protected static function ListModuleFiles($sRelDir, $sRootDir)
|
||||||
{
|
{
|
||||||
static $iDummyClassIndex = 0;
|
|
||||||
$sDirectory = $sRootDir.'/'.$sRelDir;
|
$sDirectory = $sRootDir.'/'.$sRelDir;
|
||||||
|
|
||||||
|
if (!is_dir(utils::RealPath($sDirectory, APPROOT))) {
|
||||||
|
throw new Exception('Data directory ('.$sDirectory.') Does not exist or is outside iTop.');
|
||||||
|
}
|
||||||
if ($hDir = opendir($sDirectory)) {
|
if ($hDir = opendir($sDirectory)) {
|
||||||
// This is the correct way to loop over the directory. (according to the documentation)
|
// This is the correct way to loop over the directory. (according to the documentation)
|
||||||
while (($sFile = readdir($hDir)) !== false) {
|
while (($sFile = readdir($hDir)) !== false) {
|
||||||
@@ -425,14 +427,15 @@ class ModuleDiscovery
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
SetupLog::Info("Module considered as removed", null, ['extension_code' => $oExtension->sCode, 'module_name' => $sModuleName, 'module_version' => $sModuleVersion, ModuleFileReader::MODULE_FILE_PATH => $sCurrentModuleFilePath]);
|
IssueLog::Debug("Module considered as removed", null, ['extension_code' => $oExtension->sCode, 'module_name' => $sModuleName, 'module_version' => $sModuleVersion, ModuleFileReader::MODULE_FILE_PATH => $sCurrentModuleFilePath]);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (count($aNonMatchingPaths) > 0) {
|
if (count($aNonMatchingPaths) > 0) {
|
||||||
//add log for support
|
//add log for support
|
||||||
SetupLog::Debug("Module kept as it came from non removed extensions", null, ['module_name' => $sModuleName, 'module_version' => $sModuleVersion, ModuleFileReader::MODULE_FILE_PATH => $sModuleFilePath, 'non_matching_paths' => $aNonMatchingPaths]);
|
IssueLog::Debug("Module kept as it came from non removed extensions", null, ['module_name' => $sModuleName, 'module_version' => $sModuleVersion, ModuleFileReader::MODULE_FILE_PATH => $sModuleFilePath, 'non_matching_paths' => $aNonMatchingPaths]);
|
||||||
}
|
}
|
||||||
|
IssueLog::Debug(__METHOD__.' Module loaded', null, ['name' => $sModuleName, 'version' => $sModuleVersion]);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -99,6 +99,7 @@ class RunTimeEnvironment
|
|||||||
$this->sBuildEnv = $sEnvironment.'-build';
|
$this->sBuildEnv = $sEnvironment.'-build';
|
||||||
}
|
}
|
||||||
$this->oExtensionsMap = null;
|
$this->oExtensionsMap = null;
|
||||||
|
SetupLog::Enable(APPROOT.'log/setup.log');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1576,11 +1576,13 @@ JS
|
|||||||
* @return array
|
* @return array
|
||||||
* @throws Exception
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
public static function AnalyzeInstallation($oWizard, $bAbortOnMissingDependency = false, $aModulesToLoad = null)
|
public static function AnalyzeInstallation($oWizard, $bAbortOnMissingDependency = false, $aModulesToLoad = null, Config $oConfig = null)
|
||||||
{
|
{
|
||||||
require_once(APPROOT.'/setup/moduleinstaller.class.inc.php');
|
require_once(APPROOT.'/setup/moduleinstaller.class.inc.php');
|
||||||
|
|
||||||
$oConfig = self::GetConfig($oWizard);
|
if (is_null($oConfig)) {
|
||||||
|
$oConfig = self::GetConfig($oWizard);
|
||||||
|
}
|
||||||
|
|
||||||
$aDirsToScan = [$oWizard->GetParameter('source_dir', '')];
|
$aDirsToScan = [$oWizard->GetParameter('source_dir', '')];
|
||||||
|
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ require_once(APPROOT.'setup/extensionsmap.class.inc.php');
|
|||||||
|
|
||||||
class WizardController
|
class WizardController
|
||||||
{
|
{
|
||||||
protected $aSteps;
|
protected $aWizardSteps;
|
||||||
protected $sInitialStepClass;
|
protected $sInitialStepClass;
|
||||||
protected $sInitialState;
|
protected $sInitialState;
|
||||||
protected $aParameters;
|
protected $aParameters;
|
||||||
@@ -53,7 +53,7 @@ class WizardController
|
|||||||
$this->sInitialStepClass = $sInitialStepClass;
|
$this->sInitialStepClass = $sInitialStepClass;
|
||||||
$this->sInitialState = $sInitialState;
|
$this->sInitialState = $sInitialState;
|
||||||
$this->aParameters = [];
|
$this->aParameters = [];
|
||||||
$this->aSteps = [];
|
$this->aWizardSteps = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -62,7 +62,7 @@ class WizardController
|
|||||||
*/
|
*/
|
||||||
protected function PushStep($aStepInfo)
|
protected function PushStep($aStepInfo)
|
||||||
{
|
{
|
||||||
array_push($this->aSteps, $aStepInfo);
|
array_push($this->aWizardSteps, $aStepInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -71,7 +71,7 @@ class WizardController
|
|||||||
*/
|
*/
|
||||||
protected function PopStep()
|
protected function PopStep()
|
||||||
{
|
{
|
||||||
return array_pop($this->aSteps);
|
return array_pop($this->aWizardSteps);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -235,9 +235,9 @@ HTML;
|
|||||||
$oPage->add('<input type="hidden" name="_params['.$sCode.']" value="'.utils::EscapeHtml($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->aSteps)).'"/>');
|
$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>');
|
$oPage->add('<table style="width:100%;" class="ibo-setup--wizard--buttons-container"><tr>');
|
||||||
if ((count($this->aSteps) > 0) && ($oStep->CanMoveBackward())) {
|
if ((count($this->aWizardSteps) > 0) && ($oStep->CanMoveBackward())) {
|
||||||
$oPage->add('<td style="text-align: left"><button id="btn_back" class="ibo-button ibo-is-alternative ibo-is-neutral" type="submit" name="operation" value="back"><span class="ibo-button--label">Back</span></button></td>');
|
$oPage->add('<td style="text-align: left"><button id="btn_back" class="ibo-button ibo-is-alternative ibo-is-neutral" type="submit" name="operation" value="back"><span class="ibo-button--label">Back</span></button></td>');
|
||||||
}
|
}
|
||||||
if ($oStep->CanMoveForward()) {
|
if ($oStep->CanMoveForward()) {
|
||||||
@@ -296,7 +296,7 @@ on the page's parameters
|
|||||||
|
|
||||||
$sOperation = utils::ReadParam('operation');
|
$sOperation = utils::ReadParam('operation');
|
||||||
$this->aParameters = utils::ReadParam('_params', [], false, 'raw_data');
|
$this->aParameters = utils::ReadParam('_params', [], false, 'raw_data');
|
||||||
$this->aSteps = json_decode(utils::ReadParam('_steps', '[]', false, 'raw_data'), true /* bAssoc */);
|
$this->SetWizardSteps(json_decode(utils::ReadParam('_steps', '[]', false, 'raw_data'), true));
|
||||||
|
|
||||||
switch ($sOperation) {
|
switch ($sOperation) {
|
||||||
case 'next':
|
case 'next':
|
||||||
@@ -371,6 +371,11 @@ on the page's parameters
|
|||||||
return $sOutput;
|
return $sOutput;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function SetWizardSteps(array $aWizardSteps): void
|
||||||
|
{
|
||||||
|
$this->aWizardSteps = $aWizardSteps;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $sCurrentStepClass
|
* @param string $sCurrentStepClass
|
||||||
* @param string $sCurrentState
|
* @param string $sCurrentState
|
||||||
|
|||||||
@@ -100,39 +100,30 @@ JS);
|
|||||||
{
|
{
|
||||||
$sApplicationUrl = utils::GetAbsoluteUrlModulePage('combodo-data-feature-removal', 'index.php');
|
$sApplicationUrl = utils::GetAbsoluteUrlModulePage('combodo-data-feature-removal', 'index.php');
|
||||||
|
|
||||||
$aRemovedExtensions = json_decode($this->oWizard->GetParameter('removed_extensions', '[]'), true);
|
$aParams = [
|
||||||
$aHiddenRemovedExtensionInputs = '';
|
'selected_modules',
|
||||||
if (!is_array($aRemovedExtensions)) {
|
'selected_extensions',
|
||||||
IssueLog::Warning('Posted removed_extensions is not an array');
|
'display_choices',
|
||||||
$aRemovedExtensions = [];
|
'added_extensions',
|
||||||
}
|
'removed_extensions',
|
||||||
foreach ($aRemovedExtensions as $sExtCode => $sExtLabel) {
|
'extensions_not_uninstallable',
|
||||||
$sSafeExtCode = utils::HtmlEntities($sExtCode);
|
];
|
||||||
$aHiddenRemovedExtensionInputs .= <<<INPUT
|
$aHiddenInputs = '';
|
||||||
<input type="hidden" name="aRemovedExtensions[$sSafeExtCode]" value="$sExtLabel"/>
|
foreach ($aParams as $sParamName) {
|
||||||
|
$sElements = utils::HtmlEntities($this->oWizard->GetParameter($sParamName, '[]'));
|
||||||
|
$sParamName = utils::HtmlEntities($sParamName);
|
||||||
|
$aHiddenInputs .= <<<INPUT
|
||||||
|
<input type="hidden" name="$sParamName" value="$sElements"/>
|
||||||
INPUT;
|
INPUT;
|
||||||
}
|
}
|
||||||
|
|
||||||
$aAddedExtensions = json_decode($this->oWizard->GetParameter('extensions_added', "[]"), true);
|
|
||||||
$aHiddenAddedExtensionInputs = "";
|
|
||||||
if (!is_array($aAddedExtensions)) {
|
|
||||||
IssueLog::Warning('Posted extensions_added is not an array');
|
|
||||||
$aAddedExtensions = [];
|
|
||||||
}
|
|
||||||
foreach ($aAddedExtensions as $sExtCode => $sExtLabel) {
|
|
||||||
$sSafeExtCode = utils::HtmlEntities($sExtCode);
|
|
||||||
$aHiddenAddedExtensionInputs .= <<<INPUT
|
|
||||||
<input type="hidden" name="aAddedExtensions[$sSafeExtCode]" value="$sExtLabel"/>
|
|
||||||
INPUT;
|
|
||||||
}
|
|
||||||
$sUID = Session::Get('setup_token');
|
$sUID = Session::Get('setup_token');
|
||||||
$oPage->add(
|
$oPage->add(
|
||||||
<<<HTML
|
<<<HTML
|
||||||
<form id="data-feature-removal" class="ibo-setup--wizard ibo-is-hidden" method="post" action="$sApplicationUrl">
|
<form id="data-feature-removal" class="ibo-setup--wizard ibo-is-hidden" method="post" action="$sApplicationUrl">
|
||||||
<input type="hidden" name="operation" value="AnalysisResult"/>
|
<input type="hidden" name="operation" value="AnalysisResult"/>
|
||||||
<input type="hidden" name="setup_token" value="$sUID"/>
|
<input type="hidden" name="setup_token" value="$sUID"/>
|
||||||
$aHiddenRemovedExtensionInputs
|
$aHiddenInputs
|
||||||
$aHiddenAddedExtensionInputs
|
|
||||||
</form>
|
</form>
|
||||||
HTML
|
HTML
|
||||||
);
|
);
|
||||||
@@ -141,7 +132,6 @@ HTML
|
|||||||
protected function AddProgressErrorScript($oPage, $aRes)
|
protected function AddProgressErrorScript($oPage, $aRes)
|
||||||
{
|
{
|
||||||
if (isset($aRes['error_code']) && $aRes['error_code'] === DataAuditSequencer::DATA_AUDIT_FAILED) {
|
if (isset($aRes['error_code']) && $aRes['error_code'] === DataAuditSequencer::DATA_AUDIT_FAILED) {
|
||||||
|
|
||||||
$oPage->add_ready_script(
|
$oPage->add_ready_script(
|
||||||
<<<EOF
|
<<<EOF
|
||||||
$('.ibo-setup--wizard--buttons-container tr td:nth-child(2)').before('<td style="text-align:center;"><button class="ibo-button ibo-is-alternative ibo-is-neutral" type="submit" name="operation" value="next"><span class="ibo-button--label">Ignore and continue</span></button></td>');
|
$('.ibo-setup--wizard--buttons-container tr td:nth-child(2)').before('<td style="text-align:center;"><button class="ibo-button ibo-is-alternative ibo-is-neutral" type="submit" name="operation" value="next"><span class="ibo-button--label">Ignore and continue</span></button></td>');
|
||||||
|
|||||||
95
setup/wizardsteps/WizStepLandingBeforeAudit.php
Normal file
95
setup/wizardsteps/WizStepLandingBeforeAudit.php
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @copyright Copyright (C) 2010-2026 Combodo SAS
|
||||||
|
* @license http://opensource.org/licenses/AGPL-3.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
class WizStepLandingBeforeAudit extends WizStepModulesChoice
|
||||||
|
{
|
||||||
|
public function __construct(WizardController $oWizard, $sCurrentState)
|
||||||
|
{
|
||||||
|
$oProductionEnv = new RunTimeEnvironment();
|
||||||
|
$sBuildConfigFile = APPCONF.$oProductionEnv->GetBuildEnv().'/'.ITOP_CONFIG_FILE;
|
||||||
|
$this->oConfig = new Config($sBuildConfigFile);
|
||||||
|
|
||||||
|
$oWizard->SetParameter('previous_version_dir', APPROOT);
|
||||||
|
$oWizard->SetParameter('install_mode', 'upgrade');
|
||||||
|
$oWizard->SetParameter('source_dir', APPROOT.$this->oConfig->Get('source_dir'));
|
||||||
|
$oWizard->SetParameter('graphviz_path', $this->oConfig->Get('graphviz_path'));
|
||||||
|
$oWizard->SetParameter('application_url', $this->oConfig->Get('app_root_url'));
|
||||||
|
$oWizard->SetParameter('datamodel_version', ITOP_CORE_VERSION);
|
||||||
|
$oWizard->SetParameter('upgrade_type', 'use-compatible');
|
||||||
|
|
||||||
|
$oWizard->SaveParameter('use_symbolic_links', MFCompiler::UseSymbolicLinks());
|
||||||
|
$oWizard->SaveParameter('force-uninstall', '');
|
||||||
|
|
||||||
|
// should be done at the end
|
||||||
|
parent::__construct($oWizard, $sCurrentState, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public function Display(SetupPage $oPage): void
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public function UpdateWizardStateAndGetNextStep($bMoveForward = true): WizardState
|
||||||
|
{
|
||||||
|
$oProductionEnv = new RunTimeEnvironment();
|
||||||
|
$sBuildConfigFile = APPCONF.$oProductionEnv->GetBuildEnv().'/'.ITOP_CONFIG_FILE;
|
||||||
|
@chmod($sBuildConfigFile, 0770); // In case it exists: RWX for owner and group, nothing for others
|
||||||
|
|
||||||
|
$oConfig = new Config($sBuildConfigFile);
|
||||||
|
$this->oWizard->SetParameter('db_server', $oConfig->Get('db_host'));
|
||||||
|
$this->oWizard->SetParameter('db_user', $oConfig->Get('db_user'));
|
||||||
|
$this->oWizard->SetParameter('db_pwd', $oConfig->Get('db_pwd'));
|
||||||
|
$this->oWizard->SetParameter('db_name', $oConfig->Get('db_name'));
|
||||||
|
$this->oWizard->SetParameter('db_prefix', $oConfig->Get('db_subname'));
|
||||||
|
$this->oWizard->SetParameter('db_tls_enabled', $oConfig->Get('db_tls.enabled'));
|
||||||
|
$this->oWizard->SetParameter('db_tls_ca', $oConfig->Get('db_tls.ca') ?? '');
|
||||||
|
|
||||||
|
$this->oWizard->SaveParameter('selected_modules', []);
|
||||||
|
$this->oWizard->SaveParameter('selected_extensions', []);
|
||||||
|
$this->oWizard->SaveParameter('display_choices', []);
|
||||||
|
$this->oWizard->SaveParameter('added_extensions', []);
|
||||||
|
$this->oWizard->SaveParameter('removed_extensions', []);
|
||||||
|
$this->oWizard->SaveParameter('extensions_not_uninstallable', []);
|
||||||
|
|
||||||
|
$aWizardSteps = $this->GetWizardSteps();
|
||||||
|
$this->oWizard->SetWizardSteps($aWizardSteps);
|
||||||
|
$this->sCurrentState = count($aWizardSteps) - 1;
|
||||||
|
|
||||||
|
$aSelectedComponents = $this->GetSelectedComponents($this->aSteps, $this->oWizard->GetParameter('selected_extensions'));
|
||||||
|
$this->oWizard->SetParameter('selected_components', json_encode($aSelectedComponents));
|
||||||
|
|
||||||
|
return new WizardState(WizStepDataAudit::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public function GetTitle(): string
|
||||||
|
{
|
||||||
|
return 'Before checking compatibility';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function GetPossibleSteps()
|
||||||
|
{
|
||||||
|
return [WizStepDataAudit::class];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function GetNextButtonLabel()
|
||||||
|
{
|
||||||
|
return 'Next';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function CanComeBack()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -25,7 +25,7 @@ use Combodo\iTop\Setup\ModuleDiscovery\ModuleFileReaderException;
|
|||||||
/**
|
/**
|
||||||
* Choice of the modules to be installed
|
* Choice of the modules to be installed
|
||||||
*/
|
*/
|
||||||
class WizStepModulesChoice extends WizardStep
|
class WizStepModulesChoice extends AbstractWizStepInstall
|
||||||
{
|
{
|
||||||
protected static string $SEP = '_';
|
protected static string $SEP = '_';
|
||||||
protected bool $bUpgrade = false;
|
protected bool $bUpgrade = false;
|
||||||
@@ -38,7 +38,7 @@ class WizStepModulesChoice extends WizardStep
|
|||||||
*/
|
*/
|
||||||
protected iTopExtensionsMap $oExtensionsMap;
|
protected iTopExtensionsMap $oExtensionsMap;
|
||||||
|
|
||||||
private ?array $aSteps = null;
|
protected ?array $aSteps = null;
|
||||||
|
|
||||||
protected PhpExpressionEvaluator $oPhpExpressionEvaluator;
|
protected PhpExpressionEvaluator $oPhpExpressionEvaluator;
|
||||||
|
|
||||||
@@ -51,7 +51,7 @@ class WizStepModulesChoice extends WizardStep
|
|||||||
private array $aAnalyzeInstallationModules = [];
|
private array $aAnalyzeInstallationModules = [];
|
||||||
private ?MissingDependencyException $oMissingDependencyException = null;
|
private ?MissingDependencyException $oMissingDependencyException = null;
|
||||||
|
|
||||||
public function __construct(WizardController $oWizard, $sCurrentState)
|
public function __construct(WizardController $oWizard, $sCurrentState, bool $bOverWriteConfig = true)
|
||||||
{
|
{
|
||||||
parent::__construct($oWizard, $sCurrentState);
|
parent::__construct($oWizard, $sCurrentState);
|
||||||
$this->bChoicesFromDatabase = false;
|
$this->bChoicesFromDatabase = false;
|
||||||
@@ -69,8 +69,10 @@ class WizStepModulesChoice extends WizardStep
|
|||||||
if ($sConfigPath !== null) {
|
if ($sConfigPath !== null) {
|
||||||
$this->oConfig = new Config($sConfigPath);
|
$this->oConfig = new Config($sConfigPath);
|
||||||
|
|
||||||
$aParamValues = $oWizard->GetParamForConfigArray();
|
if ($bOverWriteConfig) {
|
||||||
$this->oConfig->UpdateFromParams($aParamValues);
|
$aParamValues = $oWizard->GetParamForConfigArray();
|
||||||
|
$this->oConfig->UpdateFromParams($aParamValues);
|
||||||
|
}
|
||||||
|
|
||||||
$this->oExtensionsMap->LoadChoicesFromDatabase($this->oConfig);
|
$this->oExtensionsMap->LoadChoicesFromDatabase($this->oConfig);
|
||||||
$this->bChoicesFromDatabase = true;
|
$this->bChoicesFromDatabase = true;
|
||||||
@@ -78,7 +80,7 @@ class WizStepModulesChoice extends WizardStep
|
|||||||
|
|
||||||
// Sanity check (not stopper, to let developers go further...)
|
// Sanity check (not stopper, to let developers go further...)
|
||||||
try {
|
try {
|
||||||
$this->aAnalyzeInstallationModules = SetupUtils::AnalyzeInstallation($this->oWizard, true);
|
$this->aAnalyzeInstallationModules = SetupUtils::AnalyzeInstallation($this->oWizard, true, null, $this->oConfig);
|
||||||
} catch (MissingDependencyException $e) {
|
} catch (MissingDependencyException $e) {
|
||||||
$this->oMissingDependencyException = $e;
|
$this->oMissingDependencyException = $e;
|
||||||
$this->aAnalyzeInstallationModules = SetupUtils::AnalyzeInstallation($this->oWizard);
|
$this->aAnalyzeInstallationModules = SetupUtils::AnalyzeInstallation($this->oWizard);
|
||||||
@@ -118,17 +120,6 @@ class WizStepModulesChoice extends WizardStep
|
|||||||
return [$aExtensionsAdded, $aExtensionsRemoved, $aExtensionsNotUninstallable];
|
return [$aExtensionsAdded, $aExtensionsRemoved, $aExtensionsNotUninstallable];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function IsDataAuditEnabled(): bool
|
|
||||||
{
|
|
||||||
$sPath = APPROOT.'env-production';
|
|
||||||
if (!is_dir($sPath)) {
|
|
||||||
SetupLog::Info("Reinstallation of an iTop from a backup (No env-production found). Setup data audit disabled");
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function UpdateWizardStateAndGetNextStep($bMoveForward = true): WizardState
|
public function UpdateWizardStateAndGetNextStep($bMoveForward = true): WizardState
|
||||||
{
|
{
|
||||||
// Accumulates the selected modules:
|
// Accumulates the selected modules:
|
||||||
@@ -163,7 +154,7 @@ class WizStepModulesChoice extends WizardStep
|
|||||||
$this->oWizard->SetParameter('selected_modules', json_encode(array_keys($aModules)));
|
$this->oWizard->SetParameter('selected_modules', json_encode(array_keys($aModules)));
|
||||||
$this->oWizard->SetParameter('selected_extensions', json_encode($aExtensions));
|
$this->oWizard->SetParameter('selected_extensions', json_encode($aExtensions));
|
||||||
$this->oWizard->SetParameter('display_choices', $sDisplayChoices);
|
$this->oWizard->SetParameter('display_choices', $sDisplayChoices);
|
||||||
$this->oWizard->SetParameter('extensions_added', json_encode($aExtensionsAdded));
|
$this->oWizard->SetParameter('added_extensions', json_encode($aExtensionsAdded));
|
||||||
$this->oWizard->SetParameter('removed_extensions', json_encode($aExtensionsRemoved));
|
$this->oWizard->SetParameter('removed_extensions', json_encode($aExtensionsRemoved));
|
||||||
$this->oWizard->SetParameter('extensions_not_uninstallable', json_encode(array_keys($aExtensionsNotUninstallable)));
|
$this->oWizard->SetParameter('extensions_not_uninstallable', json_encode(array_keys($aExtensionsNotUninstallable)));
|
||||||
|
|
||||||
@@ -175,6 +166,96 @@ class WizStepModulesChoice extends WizardStep
|
|||||||
return new WizardState(WizStepModulesChoice::class, (string)($index - 1));
|
return new WizardState(WizStepModulesChoice::class, (string)($index - 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function GetWizardSteps(): array
|
||||||
|
{
|
||||||
|
$aSteps = [
|
||||||
|
["class" => "WizStepWelcome","state" => ""],
|
||||||
|
["class" => "WizStepInstallOrUpgrade","state" => ""],
|
||||||
|
["class" => "WizStepDetectedInfo","state" => ""],
|
||||||
|
["class" => "WizStepUpgradeMiscParams","state" => ""],
|
||||||
|
];
|
||||||
|
$i = 0;
|
||||||
|
while (null != $this->GetStepInfo($i)) {
|
||||||
|
$aSteps [] = ["class" => "WizStepModulesChoice","state" => "$i"];
|
||||||
|
$i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $aSteps;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function GetSelectedComponents(array $aSteps, string $sSelectedExtensionJson): array
|
||||||
|
{
|
||||||
|
SetupLog::Error(__METHOD__, null, $aSteps);
|
||||||
|
$aExtensions = json_decode($sSelectedExtensionJson, true);
|
||||||
|
$aRes = [];
|
||||||
|
foreach ($aSteps as $i => $aStepInfo) {
|
||||||
|
$aStepRes = [];
|
||||||
|
$this->ProcessOptions("", $aStepInfo, $aExtensions, $aStepRes);
|
||||||
|
$this->ProcessAlternatives("", $aStepInfo, $aExtensions, $aStepRes);
|
||||||
|
$aRes [] = $aStepRes;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $aRes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function ProcessOptions(string $sCurrentIndex, array $aInfo, array $aExtensions, array &$aStepRes)
|
||||||
|
{
|
||||||
|
$aOptions = $aInfo["options"] ?? null;
|
||||||
|
if (is_null($aOptions) || !is_array($aOptions)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($aOptions as $i => $aOptionsInfo) {
|
||||||
|
$sExtensionCode = $aOptionsInfo["extension_code"] ?? null;
|
||||||
|
|
||||||
|
if (in_array($sExtensionCode, $aExtensions)) {
|
||||||
|
$aStepRes = $this->ProcessSelectedOption($sCurrentIndex, $i, $aStepRes, $aOptionsInfo, $aExtensions);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function ProcessAlternatives(string $sCurrentIndex, array $aInfo, array $aExtensions, array &$aStepRes)
|
||||||
|
{
|
||||||
|
$aAlternatives = $aInfo["alternatives"] ?? null;
|
||||||
|
if (is_null($aAlternatives) || ! is_array($aAlternatives)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($aAlternatives as $i => $aAlternativeInfo) {
|
||||||
|
$sExtensionCode = $aAlternativeInfo["extension_code"] ?? null;
|
||||||
|
|
||||||
|
if (in_array($sExtensionCode, $aExtensions)) {
|
||||||
|
$aStepRes = $this->ProcessSelectedOption($sCurrentIndex, $i, $aStepRes, $aAlternativeInfo, $aExtensions);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $sCurrentIndex
|
||||||
|
* @param int|string $i
|
||||||
|
* @param array $aStepRes
|
||||||
|
* @param mixed $aOptionsInfo
|
||||||
|
* @param array $aExtensions
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function ProcessSelectedOption(string $sCurrentIndex, int|string $i, array $aStepRes, mixed $aOptionsInfo, array $aExtensions): array
|
||||||
|
{
|
||||||
|
$sNextIndex = "{$sCurrentIndex}_{$i}";
|
||||||
|
$aStepRes[$sNextIndex] = $sNextIndex;
|
||||||
|
|
||||||
|
$aSubOptions = $aOptionsInfo['sub_options'] ?? null;
|
||||||
|
if (!is_null($aSubOptions) && is_array($aSubOptions)) {
|
||||||
|
$this->ProcessOptions($sNextIndex, $aSubOptions, $aExtensions, $aStepRes);
|
||||||
|
$this->ProcessAlternatives($sNextIndex, $aSubOptions, $aExtensions, $aStepRes);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->ProcessAlternatives($sNextIndex, $aOptionsInfo, $aExtensions, $aStepRes);
|
||||||
|
|
||||||
|
return $aStepRes;
|
||||||
|
}
|
||||||
|
|
||||||
public function Display(SetupPage $oPage): void
|
public function Display(SetupPage $oPage): void
|
||||||
{
|
{
|
||||||
$this->DisplayStep($oPage);
|
$this->DisplayStep($oPage);
|
||||||
@@ -861,7 +942,7 @@ EOF
|
|||||||
return 'Non-uninstallable extension missing';
|
return 'Non-uninstallable extension missing';
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->GetStepInfo(1 + $this->GetStepIndex()) === null && $this->IsDataAuditEnabled()) {
|
if ($this->GetStepInfo(1 + $this->GetStepIndex()) === null) {
|
||||||
return 'Check compatibility';
|
return 'Check compatibility';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,6 @@
|
|||||||
*
|
*
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
*/
|
*/
|
||||||
use Combodo\iTop\Application\WebPage\WebPage;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Summary of the installation tasks
|
* Summary of the installation tasks
|
||||||
@@ -84,7 +83,7 @@ class WizStepSummary extends AbstractWizStepInstall
|
|||||||
$oPage->add('<div id="params_summary">');
|
$oPage->add('<div id="params_summary">');
|
||||||
|
|
||||||
$oPage->add('<div class="closed"><a class="title ibo-setup-summary-title" href="#" aria-label="Extensions to be installed">Extensions to be installed</a>');
|
$oPage->add('<div class="closed"><a class="title ibo-setup-summary-title" href="#" aria-label="Extensions to be installed">Extensions to be installed</a>');
|
||||||
$aExtensionsAdded = json_decode($this->oWizard->GetParameter('extensions_added'), true);
|
$aExtensionsAdded = json_decode($this->oWizard->GetParameter('added_extensions'), true) ?? [];
|
||||||
|
|
||||||
if (count($aExtensionsAdded) > 0) {
|
if (count($aExtensionsAdded) > 0) {
|
||||||
$sExtensionsAdded = '<ul>';
|
$sExtensionsAdded = '<ul>';
|
||||||
|
|||||||
@@ -18,4 +18,5 @@ require_once(APPROOT.'setup/wizardsteps/WizStepInstallMiscParams.php');
|
|||||||
require_once(APPROOT.'setup/wizardsteps/WizStepModulesChoice.php');
|
require_once(APPROOT.'setup/wizardsteps/WizStepModulesChoice.php');
|
||||||
require_once(APPROOT.'setup/wizardsteps/WizStepSummary.php');
|
require_once(APPROOT.'setup/wizardsteps/WizStepSummary.php');
|
||||||
require_once(APPROOT.'setup/wizardsteps/WizStepUpgradeMiscParams.php');
|
require_once(APPROOT.'setup/wizardsteps/WizStepUpgradeMiscParams.php');
|
||||||
|
require_once(APPROOT.'setup/wizardsteps/WizStepLandingBeforeAudit.php');
|
||||||
require_once(APPROOT.'setup/wizardcontroller.class.inc.php');
|
require_once(APPROOT.'setup/wizardcontroller.class.inc.php');
|
||||||
|
|||||||
@@ -24,6 +24,8 @@ class Form extends UIContentBlock
|
|||||||
protected $sOnSubmitJsCode;
|
protected $sOnSubmitJsCode;
|
||||||
/** @var string */
|
/** @var string */
|
||||||
protected $sAction;
|
protected $sAction;
|
||||||
|
/** @var string */
|
||||||
|
protected $sEncType = "multipart/form-data";
|
||||||
|
|
||||||
public function __construct(?string $sId = null)
|
public function __construct(?string $sId = null)
|
||||||
{
|
{
|
||||||
@@ -65,4 +67,25 @@ class Form extends UIContentBlock
|
|||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Override default enctype (default : "multipart/form-data")
|
||||||
|
* @param string $sEncType
|
||||||
|
* @return $this
|
||||||
|
* @since 3.3.0
|
||||||
|
*/
|
||||||
|
public function SetEncType(string $sEncType)
|
||||||
|
{
|
||||||
|
$this->sEncType = $sEncType;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
* @since 3.3.0
|
||||||
|
*/
|
||||||
|
public function GetEncType(): string
|
||||||
|
{
|
||||||
|
return $this->sEncType;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<form method="post" enctype="multipart/form-data" id="{{ oUIBlock.GetId() }}"
|
<form method="post" enctype="{{ oUIBlock.GetEncType() }}" id="{{ oUIBlock.GetId() }}"
|
||||||
class="{{ oUIBlock.GetBlocksInheritanceCSSClassesAsString() }} {{ oUIBlock.GetAdditionalCSSClassesAsString() }} {% if oUIBlock.IsHidden() %}ibo-is-hidden{% endif %}"
|
class="{{ oUIBlock.GetBlocksInheritanceCSSClassesAsString() }} {{ oUIBlock.GetAdditionalCSSClassesAsString() }} {% if oUIBlock.IsHidden() %}ibo-is-hidden{% endif %}"
|
||||||
data-role="ibo-form"
|
data-role="ibo-form"
|
||||||
{% if oUIBlock.GetOnSubmitJsCode() %}
|
{% if oUIBlock.GetOnSubmitJsCode() %}
|
||||||
|
|||||||
@@ -107,6 +107,59 @@ class DataCleanupServiceTest extends ItopCustomDatamodelTestCase
|
|||||||
$this->AssertSummaryEquals($aExpected, $aRes);
|
$this->AssertSummaryEquals($aExpected, $aRes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testExecuteCleanup_CheckSummaryIsEnrichedAfterEachPass()
|
||||||
|
{
|
||||||
|
$aExecutionSummary = [];
|
||||||
|
|
||||||
|
$this->GivenDFRTreeInDB(
|
||||||
|
<<<EOF
|
||||||
|
DFRToRemoveLeaf_1 <- DFRToUpdate_1
|
||||||
|
DFRToRemoveLeaf_1 <- DFRRemovedCollateral_1
|
||||||
|
DFRRemovedCollateral_1 <- DFRRemovedCollateralCascade_1
|
||||||
|
|
||||||
|
DFRToRemoveLeaf_2 <- DFRToUpdate_2
|
||||||
|
DFRToRemoveLeaf_2 <- DFRRemovedCollateral_2
|
||||||
|
DFRRemovedCollateral_2 <- DFRRemovedCollateralCascade_2
|
||||||
|
|
||||||
|
DFRToRemoveLeaf_3 <- DFRToUpdate_3
|
||||||
|
DFRToRemoveLeaf_3 <- DFRRemovedCollateral_3
|
||||||
|
DFRRemovedCollateral_3 <- DFRRemovedCollateralCascade_3
|
||||||
|
EOF
|
||||||
|
);
|
||||||
|
|
||||||
|
$aClasses = ['DFRToRemoveLeaf'];
|
||||||
|
$oService = new DataCleanupService();
|
||||||
|
$aExecutionSummary = $oService->ExecuteCleanup($aClasses);
|
||||||
|
|
||||||
|
$aExpected = [
|
||||||
|
['DFRToUpdate', 3, 0 ],
|
||||||
|
['DFRToRemoveLeaf', 0, 3 ],
|
||||||
|
['DFRRemovedCollateral', 0, 3 ],
|
||||||
|
['DFRRemovedCollateralCascade', 0, 3 ],
|
||||||
|
];
|
||||||
|
$this->AssertSummaryEquals($aExpected, $aExecutionSummary);
|
||||||
|
|
||||||
|
$this->GivenDFRTreeInDB(
|
||||||
|
<<<EOF
|
||||||
|
DFRToRemoveLeaf_4 <- DFRToUpdate_4
|
||||||
|
DFRToRemoveLeaf_4 <- DFRRemovedCollateral_4
|
||||||
|
DFRRemovedCollateral_4 <- DFRRemovedCollateralCascade_4
|
||||||
|
EOF
|
||||||
|
);
|
||||||
|
|
||||||
|
$aClasses = ['DFRToRemoveLeaf'];
|
||||||
|
$oService = new DataCleanupService();
|
||||||
|
$aExecutionSummary = $oService->ExecuteCleanup($aClasses);
|
||||||
|
|
||||||
|
$aExpected = [
|
||||||
|
['DFRToUpdate', 1, 0, 0, 4 ],
|
||||||
|
['DFRToRemoveLeaf', 0, 1, 0, 0, 4 ],
|
||||||
|
['DFRRemovedCollateral', 0, 1, 0, 0, 4 ],
|
||||||
|
['DFRRemovedCollateralCascade', 0, 1, 0, 0, 4 ],
|
||||||
|
];
|
||||||
|
$this->AssertSummaryEquals($aExpected, $aExecutionSummary);
|
||||||
|
}
|
||||||
|
|
||||||
public function testGetCleanupSummary_DeleteManyObjPerClassWithoutLimit()
|
public function testGetCleanupSummary_DeleteManyObjPerClassWithoutLimit()
|
||||||
{
|
{
|
||||||
$this->GivenDFRTreeInDB(<<<EOF
|
$this->GivenDFRTreeInDB(<<<EOF
|
||||||
@@ -174,11 +227,16 @@ class DataCleanupServiceTest extends ItopCustomDatamodelTestCase
|
|||||||
$iUpdate = $line[1];
|
$iUpdate = $line[1];
|
||||||
$iDelete = $line[2];
|
$iDelete = $line[2];
|
||||||
$iIssue = $line[3] ?? 0;
|
$iIssue = $line[3] ?? 0;
|
||||||
|
$iTotalUpdate = $line[4] ?? $iUpdate;
|
||||||
|
$iTotalDelete = $line[5] ?? $iDelete;
|
||||||
|
|
||||||
$oCleanupSummaryEntity = new DataCleanupSummaryEntity($sClass);
|
$oCleanupSummaryEntity = new DataCleanupSummaryEntity($sClass);
|
||||||
$oCleanupSummaryEntity->iUpdateCount = $iUpdate;
|
$oCleanupSummaryEntity->iUpdateCount = $iUpdate;
|
||||||
$oCleanupSummaryEntity->iDeleteCount = $iDelete;
|
$oCleanupSummaryEntity->iDeleteCount = $iDelete;
|
||||||
$oCleanupSummaryEntity->iIssueCount = $iIssue;
|
$oCleanupSummaryEntity->iIssueCount = $iIssue;
|
||||||
|
$oCleanupSummaryEntity->iTotalUpdateCount = $iTotalUpdate;
|
||||||
|
$oCleanupSummaryEntity->iTotalDeleteCount = $iTotalDelete;
|
||||||
|
|
||||||
$aExpected[$sClass] = $oCleanupSummaryEntity;
|
$aExpected[$sClass] = $oCleanupSummaryEntity;
|
||||||
}
|
}
|
||||||
$this->assertEquals($aExpected, $actual, $sMessage);
|
$this->assertEquals($aExpected, $actual, $sMessage);
|
||||||
@@ -270,18 +328,18 @@ class DataCleanupServiceTest extends ItopCustomDatamodelTestCase
|
|||||||
private array $aIdByObjectName = [];
|
private array $aIdByObjectName = [];
|
||||||
private function GivenDFRTreeLineInDB(string $sLine)
|
private function GivenDFRTreeLineInDB(string $sLine)
|
||||||
{
|
{
|
||||||
list($sLeft, $sRight) = explode('<-', $sLine);
|
[$sLeft, $sRight] = explode('<-', $sLine);
|
||||||
$sLeft = trim($sLeft);
|
$sLeft = trim($sLeft);
|
||||||
|
|
||||||
$iLeftId = $this->aIdByObjectName[$sLeft] ?? 0;
|
$iLeftId = $this->aIdByObjectName[$sLeft] ?? 0;
|
||||||
if ($iLeftId === 0) {
|
if ($iLeftId === 0) {
|
||||||
list($sChildClass, ) = explode('_', $sLeft, 2);
|
[$sChildClass, ] = explode('_', $sLeft, 2);
|
||||||
$iLeftId = $this->GivenObjectInDB($sChildClass, ['name' => $sLeft]);
|
$iLeftId = $this->GivenObjectInDB($sChildClass, ['name' => $sLeft]);
|
||||||
$this->aIdByObjectName[$sLeft] = $iLeftId;
|
$this->aIdByObjectName[$sLeft] = $iLeftId;
|
||||||
}
|
}
|
||||||
|
|
||||||
$sRight = trim($sRight);
|
$sRight = trim($sRight);
|
||||||
list($sChildClass, ) = explode('_', $sRight, 2);
|
[$sChildClass, ] = explode('_', $sRight, 2);
|
||||||
$iRightId = $this->GivenObjectInDB($sChildClass, ['name' => $sRight, 'extkey_id' => $iLeftId]);
|
$iRightId = $this->GivenObjectInDB($sChildClass, ['name' => $sRight, 'extkey_id' => $iLeftId]);
|
||||||
$this->aIdByObjectName[$sRight] = $iRightId;
|
$this->aIdByObjectName[$sRight] = $iRightId;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,125 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Combodo\iTop\Application\WebPage\NiceWebPage;
|
||||||
|
|
||||||
|
require_once(dirname(__DIR__, 6).'/approot.inc.php');
|
||||||
|
require_once(APPROOT.'application/startup.inc.php');
|
||||||
|
require_once(APPROOT.'setup/setuputils.class.inc.php');
|
||||||
|
|
||||||
|
$aParams = [
|
||||||
|
"exec_module" => "combodo-data-feature-removal",
|
||||||
|
"exec_page" => "index.php",
|
||||||
|
'exec_env' => 'production',
|
||||||
|
];
|
||||||
|
|
||||||
|
new ContextTag(ContextTag::TAG_SETUP);
|
||||||
|
$sToken = SetupUtils::CreateSetupToken();
|
||||||
|
|
||||||
|
$aSelectedModules = [
|
||||||
|
'authent-cas',
|
||||||
|
'authent-external',
|
||||||
|
'authent-ldap',
|
||||||
|
'authent-local',
|
||||||
|
'combodo-backoffice-darkmoon-theme',
|
||||||
|
'combodo-backoffice-fullmoon-high-contrast-theme',
|
||||||
|
'combodo-backoffice-fullmoon-protanopia-deuteranopia-theme',
|
||||||
|
'combodo-backoffice-fullmoon-tritanopia-theme',
|
||||||
|
'combodo-data-feature-removal',
|
||||||
|
'itop-backup',
|
||||||
|
'itop-config',
|
||||||
|
'itop-files-information',
|
||||||
|
'itop-portal-base',
|
||||||
|
'itop-profiles-itil',
|
||||||
|
'itop-sla-computation',
|
||||||
|
'itop-structure',
|
||||||
|
'itop-welcome-itil',
|
||||||
|
'itop-config-mgmt',
|
||||||
|
'itop-attachments',
|
||||||
|
'itop-tickets',
|
||||||
|
'combodo-db-tools',
|
||||||
|
'itop-core-update',
|
||||||
|
'itop-hub-connector',
|
||||||
|
'itop-oauth-client',
|
||||||
|
'itop-themes-compat',
|
||||||
|
'combodo-my-account',
|
||||||
|
'combodo-my-account-user-info',
|
||||||
|
'combodo-oauth2-client',
|
||||||
|
'itop-attribute-class-set',
|
||||||
|
'itop-attribute-encrypted-password',
|
||||||
|
'itop-ui-copypaste',
|
||||||
|
'itop-datacenter-mgmt',
|
||||||
|
'itop-endusers-devices',
|
||||||
|
'itop-storage-mgmt',
|
||||||
|
'itop-virtualization-mgmt',
|
||||||
|
'itop-bridge-cmdb-ticket',
|
||||||
|
'itop-bridge-virtualization-storage',
|
||||||
|
'itop-service-mgmt',
|
||||||
|
'itop-bridge-cmdb-services',
|
||||||
|
'itop-bridge-datacenter-mgmt-services',
|
||||||
|
'itop-bridge-endusers-devices-services',
|
||||||
|
'itop-bridge-storage-mgmt-services',
|
||||||
|
'itop-bridge-virtualization-mgmt-services',
|
||||||
|
'itop-request-mgmt',
|
||||||
|
'itop-portal',
|
||||||
|
'itop-change-mgmt',
|
||||||
|
'itop-faq-light',
|
||||||
|
'itop-knownerror-mgmt',
|
||||||
|
'itop-problem-mgmt',
|
||||||
|
'itop-system-information',
|
||||||
|
'itop-log-mgmt',
|
||||||
|
];
|
||||||
|
|
||||||
|
$aSelectedExtensions = [
|
||||||
|
'itop-config-mgmt-core',
|
||||||
|
'itop-config-mgmt-datacenter',
|
||||||
|
'itop-config-mgmt-end-user',
|
||||||
|
'itop-config-mgmt-storage',
|
||||||
|
'itop-config-mgmt-virtualization',
|
||||||
|
'itop-service-mgmt-enterprise',
|
||||||
|
'itop-ticket-mgmt-simple-ticket',
|
||||||
|
'itop-ticket-mgmt-simple-ticket-enhanced-portal',
|
||||||
|
'itop-change-mgmt-simple',
|
||||||
|
'itop-kown-error-mgmt',
|
||||||
|
'itop-problem-mgmt',
|
||||||
|
'itop-system-information',
|
||||||
|
'itop-log-mgmt',
|
||||||
|
];
|
||||||
|
|
||||||
|
$aRemovedExtensions = ['itop-container-mgmt' => 'Containerization'];
|
||||||
|
|
||||||
|
$aPostParams = [
|
||||||
|
"auth_user" => 'admin',
|
||||||
|
"auth_pwd" => 'admin',
|
||||||
|
'login_mode' => 'form',
|
||||||
|
'operation' => 'AnalysisResult',
|
||||||
|
'setup_token' => $sToken,
|
||||||
|
'selected_modules' => utils::HtmlEntities(json_encode($aSelectedModules)),
|
||||||
|
'selected_extensions' => utils::HtmlEntities(json_encode($aSelectedExtensions)),
|
||||||
|
'removed_extensions' => utils::HtmlEntities(json_encode($aRemovedExtensions)),
|
||||||
|
];
|
||||||
|
|
||||||
|
$sHiddenPostedInput = "";
|
||||||
|
foreach ($aPostParams as $sKey => $sVal) {
|
||||||
|
$sHiddenPostedInput .= <<<INPUT
|
||||||
|
<input type="hidden" name="$sKey" value="$sVal">
|
||||||
|
INPUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
$sRedirectURL = utils::GetAbsoluteUrlModulePage('combodo-data-feature-removal', 'index.php');
|
||||||
|
|
||||||
|
$sDiv = <<<DIV
|
||||||
|
<div id="_test" style="display: none;">
|
||||||
|
<form id="_form" action="$sRedirectURL" method="post">
|
||||||
|
$sHiddenPostedInput
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
DIV;
|
||||||
|
|
||||||
|
$sReadyJs = <<<JS
|
||||||
|
$("#_form").trigger("submit");
|
||||||
|
JS;
|
||||||
|
|
||||||
|
$oP = new NiceWebPage("Simulate Audit From Setup");
|
||||||
|
$oP->add($sDiv);
|
||||||
|
$oP->add_ready_script($sReadyJs);
|
||||||
|
$oP->output();
|
||||||
@@ -151,4 +151,35 @@ class ExtensionsMapTest extends ItopTestCase
|
|||||||
$this->SetNonPublicProperty($oExtensionsMap, $mapKeyInItopExtensionMap, $aMap);
|
$this->SetNonPublicProperty($oExtensionsMap, $mapKeyInItopExtensionMap, $aMap);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testiTopExtensionsMapInit()
|
||||||
|
{
|
||||||
|
$oiTopExtensionsMap = new iTopExtensionsMap(sAppRootForTests:__DIR__."/ressources");
|
||||||
|
|
||||||
|
//file_put_contents(__DIR__.'/ressources/all_extensions_from_datamodels.json', json_encode($this->SerializeExtensionMap($oiTopExtensionsMap), JSON_PRETTY_PRINT));
|
||||||
|
|
||||||
|
$sExpected = file_get_contents(__DIR__.'/ressources/all_extensions_from_datamodels.json');
|
||||||
|
$sExpected = str_replace('"sVersion": "ITOP_VERSION"', '"sVersion": "'.ITOP_VERSION.'"', $sExpected);
|
||||||
|
$sExpected = preg_replace('/"module_file_path": .*/', '"module_file_path": ANYPATH', $sExpected);
|
||||||
|
|
||||||
|
$actual = json_encode($this->SerializeExtensionMap($oiTopExtensionsMap), JSON_PRETTY_PRINT);
|
||||||
|
$actual = preg_replace('/"module_file_path": .*/', '"module_file_path": ANYPATH', $actual);
|
||||||
|
$this->assertEquals($sExpected, $actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function SerializeExtensionMap(iTopExtensionsMap $oiTopExtensionsMap): array
|
||||||
|
{
|
||||||
|
$aRes = [];
|
||||||
|
foreach ($oiTopExtensionsMap->GetAllExtensions() as $oExtension) {
|
||||||
|
$aRes[] = [
|
||||||
|
'sCode' => $oExtension->sCode,
|
||||||
|
'sSource' => $oExtension->sSource,
|
||||||
|
'sVersion' => $oExtension->sVersion,
|
||||||
|
'aModules' => $oExtension->aModules,
|
||||||
|
'aModuleVersion' => $oExtension->aModuleVersion,
|
||||||
|
'aModuleInfo' => $oExtension->aModuleInfo,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $aRes;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1391,4 +1391,16 @@ HTML,
|
|||||||
|
|
||||||
$this->assertEquals($sExpectedHTML, $oPage->sContent);
|
$this->assertEquals($sExpectedHTML, $oPage->sContent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testGetSelectedComponents()
|
||||||
|
{
|
||||||
|
$aParams = new XMLParameters(__DIR__.'/ressources/installation_330.xml');
|
||||||
|
$aSteps = $aParams->Get('steps', []);
|
||||||
|
|
||||||
|
$aSelectedExtensions = ["itop-config-mgmt-core","itop-config-mgmt-datacenter","itop-config-mgmt-end-user","itop-config-mgmt-storage","itop-config-mgmt-virtualization","itop-container-mgmt","itop-service-mgmt-enterprise","itop-ticket-mgmt-simple-ticket","itop-ticket-mgmt-simple-ticket-enhanced-portal","itop-change-mgmt-simple","itop-kown-error-mgmt","itop-problem-mgmt","combodo-oauth2-client","combodo-mfa-extended","combodo-data-replication","combodo-api-playground","combodo-snapshot"];
|
||||||
|
$aRes = $this->oStep->GetSelectedComponents($aSteps, json_encode($aSelectedExtensions));
|
||||||
|
|
||||||
|
$aExpected = json_decode('[{"_0":"_0","_1":"_1","_2":"_2","_3":"_3","_4":"_4","_4_0":"_4_0"},{"_0":"_0"},{"_0":"_0","_0_0":"_0_0"},{"_0":"_0"},{"_0":"_0","_1":"_1"}]', true);
|
||||||
|
$this->assertEquals($aExpected, $aRes);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,316 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"sCode": "itop-config-mgmt-core",
|
||||||
|
"sSource": "datamodels",
|
||||||
|
"sVersion": "ITOP_VERSION",
|
||||||
|
"aModules": [
|
||||||
|
"itop-structure",
|
||||||
|
"itop-config-mgmt",
|
||||||
|
"itop-attachments",
|
||||||
|
"itop-profiles-itil",
|
||||||
|
"itop-welcome-itil",
|
||||||
|
"itop-tickets",
|
||||||
|
"itop-files-information",
|
||||||
|
"combodo-db-tools",
|
||||||
|
"itop-core-update",
|
||||||
|
"itop-hub-connector",
|
||||||
|
"itop-oauth-client",
|
||||||
|
"combodo-backoffice-darkmoon-theme",
|
||||||
|
"combodo-backoffice-fullmoon-high-contrast-theme",
|
||||||
|
"combodo-backoffice-fullmoon-protanopia-deuteranopia-theme",
|
||||||
|
"combodo-backoffice-fullmoon-tritanopia-theme",
|
||||||
|
"itop-themes-compat",
|
||||||
|
"combodo-my-account",
|
||||||
|
"combodo-my-account-user-info",
|
||||||
|
"combodo-oauth2-client",
|
||||||
|
"itop-attribute-class-set",
|
||||||
|
"itop-attribute-encrypted-password",
|
||||||
|
"itop-ui-copypaste"
|
||||||
|
],
|
||||||
|
"aModuleVersion": {
|
||||||
|
"itop-structure": "3.3.0",
|
||||||
|
"itop-config-mgmt": "3.3.0"
|
||||||
|
},
|
||||||
|
"aModuleInfo": {
|
||||||
|
"itop-structure": {
|
||||||
|
"label": "Core iTop Structure",
|
||||||
|
"category": "business",
|
||||||
|
"dependencies": [],
|
||||||
|
"mandatory": true,
|
||||||
|
"visible": false,
|
||||||
|
"installer": "StructureInstaller",
|
||||||
|
"datamodel": [
|
||||||
|
"main.itop-structure.php"
|
||||||
|
],
|
||||||
|
"data.struct": [],
|
||||||
|
"data.sample": [
|
||||||
|
"data\/data.sample.organizations.xml",
|
||||||
|
"data\/data.sample.locations.xml",
|
||||||
|
"data\/data.sample.persons.xml",
|
||||||
|
"data\/data.sample.teams.xml",
|
||||||
|
"data\/data.sample.contactteam.xml",
|
||||||
|
"data\/data.sample.contacttype.xml"
|
||||||
|
],
|
||||||
|
"doc.manual_setup": "",
|
||||||
|
"doc.more_information": "",
|
||||||
|
"settings": [],
|
||||||
|
"module_file_path": "APPROOTtests\/php-unit-tests\/unitary-tests\/setup\/ressources\/datamodels\/2.x\/itop-structure\/module.itop-structure.php"
|
||||||
|
},
|
||||||
|
"itop-config-mgmt": {
|
||||||
|
"label": "Configuration Management (CMDB)",
|
||||||
|
"category": "business",
|
||||||
|
"dependencies": [
|
||||||
|
"itop-structure\/2.7.1"
|
||||||
|
],
|
||||||
|
"mandatory": false,
|
||||||
|
"visible": true,
|
||||||
|
"installer": "ConfigMgmtInstaller",
|
||||||
|
"datamodel": [
|
||||||
|
"model.itop-config-mgmt.php",
|
||||||
|
"main.itop-config-mgmt.php"
|
||||||
|
],
|
||||||
|
"data.struct": [
|
||||||
|
"data\/en_us.data.itop-brand.xml",
|
||||||
|
"data\/en_us.data.itop-networkdevicetype.xml",
|
||||||
|
"data\/en_us.data.itop-osfamily.xml",
|
||||||
|
"data\/en_us.data.itop-osversion.xml"
|
||||||
|
],
|
||||||
|
"data.sample": [
|
||||||
|
"data\/data.sample.model.xml",
|
||||||
|
"data\/data.sample.networkdevicetype.xml",
|
||||||
|
"data\/data.sample.servers.xml",
|
||||||
|
"data\/data.sample.nw-devices.xml",
|
||||||
|
"data\/data.sample.software.xml",
|
||||||
|
"data\/data.sample.dbserver.xml",
|
||||||
|
"data\/data.sample.dbschema.xml",
|
||||||
|
"data\/data.sample.webserver.xml",
|
||||||
|
"data\/data.sample.webapp.xml",
|
||||||
|
"data\/data.sample.applications.xml",
|
||||||
|
"data\/data.sample.applicationsolutionci.xml"
|
||||||
|
],
|
||||||
|
"doc.manual_setup": "",
|
||||||
|
"doc.more_information": "",
|
||||||
|
"settings": [],
|
||||||
|
"module_file_path": "APPROOT\/tests\/php-unit-tests\/unitary-tests\/setup\/ressources\/datamodels\/2.x\/itop-config-mgmt\/module.itop-config-mgmt.php"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"sCode": "itop-config-mgmt-datacenter",
|
||||||
|
"sSource": "datamodels",
|
||||||
|
"sVersion": "ITOP_VERSION",
|
||||||
|
"aModules": [
|
||||||
|
"itop-datacenter-mgmt"
|
||||||
|
],
|
||||||
|
"aModuleVersion": [],
|
||||||
|
"aModuleInfo": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"sCode": "itop-config-mgmt-end-user",
|
||||||
|
"sSource": "datamodels",
|
||||||
|
"sVersion": "ITOP_VERSION",
|
||||||
|
"aModules": [
|
||||||
|
"itop-endusers-devices"
|
||||||
|
],
|
||||||
|
"aModuleVersion": [],
|
||||||
|
"aModuleInfo": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"sCode": "itop-config-mgmt-storage",
|
||||||
|
"sSource": "datamodels",
|
||||||
|
"sVersion": "ITOP_VERSION",
|
||||||
|
"aModules": [
|
||||||
|
"itop-storage-mgmt"
|
||||||
|
],
|
||||||
|
"aModuleVersion": [],
|
||||||
|
"aModuleInfo": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"sCode": "itop-container-mgmt",
|
||||||
|
"sSource": "datamodels",
|
||||||
|
"sVersion": "ITOP_VERSION",
|
||||||
|
"aModules": [
|
||||||
|
"itop-container-mgmt"
|
||||||
|
],
|
||||||
|
"aModuleVersion": [],
|
||||||
|
"aModuleInfo": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"sCode": "itop-config-mgmt-virtualization",
|
||||||
|
"sSource": "datamodels",
|
||||||
|
"sVersion": "ITOP_VERSION",
|
||||||
|
"aModules": [
|
||||||
|
"itop-virtualization-mgmt"
|
||||||
|
],
|
||||||
|
"aModuleVersion": [],
|
||||||
|
"aModuleInfo": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"sCode": "itop-service-mgmt-enterprise",
|
||||||
|
"sSource": "datamodels",
|
||||||
|
"sVersion": "ITOP_VERSION",
|
||||||
|
"aModules": [
|
||||||
|
"itop-service-mgmt"
|
||||||
|
],
|
||||||
|
"aModuleVersion": {
|
||||||
|
"itop-service-mgmt": "3.3.0"
|
||||||
|
},
|
||||||
|
"aModuleInfo": {
|
||||||
|
"itop-service-mgmt": {
|
||||||
|
"label": "Service Management",
|
||||||
|
"category": "business",
|
||||||
|
"dependencies": [
|
||||||
|
"itop-tickets\/2.0.0"
|
||||||
|
],
|
||||||
|
"mandatory": false,
|
||||||
|
"visible": true,
|
||||||
|
"installer": "ServiceMgmtInstaller",
|
||||||
|
"datamodel": [],
|
||||||
|
"data.struct": [],
|
||||||
|
"data.sample": [
|
||||||
|
"data\/data.sample.organizations.xml",
|
||||||
|
"data\/data.sample.contracts.xml",
|
||||||
|
"data\/data.sample.servicefamilies.xml",
|
||||||
|
"data\/data.sample.services.xml",
|
||||||
|
"data\/data.sample.serviceelements.xml",
|
||||||
|
"data\/data.sample.sla.xml",
|
||||||
|
"data\/data.sample.slt.xml",
|
||||||
|
"data\/data.sample.sltsla.xml",
|
||||||
|
"data\/data.sample.contractservice.xml",
|
||||||
|
"data\/data.sample.deliverymodelcontact.xml"
|
||||||
|
],
|
||||||
|
"doc.manual_setup": "",
|
||||||
|
"doc.more_information": "",
|
||||||
|
"settings": [],
|
||||||
|
"module_file_path": "APPROOT\/tests\/php-unit-tests\/unitary-tests\/setup\/ressources\/datamodels\/2.x\/itop-service-mgmt\/module.itop-service-mgmt.php"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"sCode": "itop-service-mgmt-service-provider",
|
||||||
|
"sSource": "datamodels",
|
||||||
|
"sVersion": "ITOP_VERSION",
|
||||||
|
"aModules": [
|
||||||
|
"itop-service-mgmt-provider"
|
||||||
|
],
|
||||||
|
"aModuleVersion": [],
|
||||||
|
"aModuleInfo": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"sCode": "itop-ticket-mgmt-simple-ticket-enhanced-portal",
|
||||||
|
"sSource": "datamodels",
|
||||||
|
"sVersion": "ITOP_VERSION",
|
||||||
|
"aModules": [
|
||||||
|
"itop-portal",
|
||||||
|
"itop-portal-base"
|
||||||
|
],
|
||||||
|
"aModuleVersion": [],
|
||||||
|
"aModuleInfo": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"sCode": "itop-ticket-mgmt-simple-ticket",
|
||||||
|
"sSource": "datamodels",
|
||||||
|
"sVersion": "ITOP_VERSION",
|
||||||
|
"aModules": [
|
||||||
|
"itop-request-mgmt"
|
||||||
|
],
|
||||||
|
"aModuleVersion": [],
|
||||||
|
"aModuleInfo": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"sCode": "itop-ticket-mgmt-itil-user-request",
|
||||||
|
"sSource": "datamodels",
|
||||||
|
"sVersion": "ITOP_VERSION",
|
||||||
|
"aModules": [
|
||||||
|
"itop-request-mgmt-itil"
|
||||||
|
],
|
||||||
|
"aModuleVersion": [],
|
||||||
|
"aModuleInfo": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"sCode": "itop-ticket-mgmt-itil-incident",
|
||||||
|
"sSource": "datamodels",
|
||||||
|
"sVersion": "ITOP_VERSION",
|
||||||
|
"aModules": [
|
||||||
|
"itop-incident-mgmt-itil"
|
||||||
|
],
|
||||||
|
"aModuleVersion": [],
|
||||||
|
"aModuleInfo": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"sCode": "itop-ticket-mgmt-itil-enhanced-portal",
|
||||||
|
"sSource": "datamodels",
|
||||||
|
"sVersion": "ITOP_VERSION",
|
||||||
|
"aModules": [
|
||||||
|
"itop-portal",
|
||||||
|
"itop-portal-base"
|
||||||
|
],
|
||||||
|
"aModuleVersion": [],
|
||||||
|
"aModuleInfo": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"sCode": "itop-ticket-mgmt-itil",
|
||||||
|
"sSource": "datamodels",
|
||||||
|
"sVersion": "ITOP_VERSION",
|
||||||
|
"aModules": [],
|
||||||
|
"aModuleVersion": [],
|
||||||
|
"aModuleInfo": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"sCode": "itop-ticket-mgmt-none",
|
||||||
|
"sSource": "datamodels",
|
||||||
|
"sVersion": "ITOP_VERSION",
|
||||||
|
"aModules": [],
|
||||||
|
"aModuleVersion": [],
|
||||||
|
"aModuleInfo": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"sCode": "itop-change-mgmt-simple",
|
||||||
|
"sSource": "datamodels",
|
||||||
|
"sVersion": "ITOP_VERSION",
|
||||||
|
"aModules": [
|
||||||
|
"itop-change-mgmt"
|
||||||
|
],
|
||||||
|
"aModuleVersion": [],
|
||||||
|
"aModuleInfo": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"sCode": "itop-change-mgmt-itil",
|
||||||
|
"sSource": "datamodels",
|
||||||
|
"sVersion": "ITOP_VERSION",
|
||||||
|
"aModules": [
|
||||||
|
"itop-change-mgmt-itil"
|
||||||
|
],
|
||||||
|
"aModuleVersion": [],
|
||||||
|
"aModuleInfo": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"sCode": "itop-change-mgmt-none",
|
||||||
|
"sSource": "datamodels",
|
||||||
|
"sVersion": "ITOP_VERSION",
|
||||||
|
"aModules": [],
|
||||||
|
"aModuleVersion": [],
|
||||||
|
"aModuleInfo": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"sCode": "itop-kown-error-mgmt",
|
||||||
|
"sSource": "datamodels",
|
||||||
|
"sVersion": "ITOP_VERSION",
|
||||||
|
"aModules": [
|
||||||
|
"itop-faq-light",
|
||||||
|
"itop-knownerror-mgmt"
|
||||||
|
],
|
||||||
|
"aModuleVersion": [],
|
||||||
|
"aModuleInfo": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"sCode": "itop-problem-mgmt",
|
||||||
|
"sSource": "datamodels",
|
||||||
|
"sVersion": "ITOP_VERSION",
|
||||||
|
"aModules": [
|
||||||
|
"itop-problem-mgmt"
|
||||||
|
],
|
||||||
|
"aModuleVersion": [],
|
||||||
|
"aModuleInfo": []
|
||||||
|
}
|
||||||
|
]
|
||||||
@@ -0,0 +1,243 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<installation>
|
||||||
|
<steps type="array">
|
||||||
|
<step>
|
||||||
|
<title>Configuration Management options</title>
|
||||||
|
<description><![CDATA[<h2>The options below allow you to configure the type of elements that are to be managed inside iTop.</h2>]]></description>
|
||||||
|
<banner>/images/icons/icons8-apps-tab.svg</banner>
|
||||||
|
<options type="array">
|
||||||
|
<choice>
|
||||||
|
<extension_code>itop-config-mgmt-core</extension_code>
|
||||||
|
<title>Configuration Management Core</title>
|
||||||
|
<description>All the base objects that are mandatory in the iTop CMDB: Organizations, Locations, Teams, Persons, etc.</description>
|
||||||
|
<modules type="array">
|
||||||
|
<module>itop-structure</module>
|
||||||
|
<module>itop-config-mgmt</module>
|
||||||
|
<module>itop-attachments</module>
|
||||||
|
<module>itop-profiles-itil</module>
|
||||||
|
<module>itop-welcome-itil</module>
|
||||||
|
<module>itop-tickets</module>
|
||||||
|
<module>itop-files-information</module>
|
||||||
|
<module>combodo-db-tools</module>
|
||||||
|
<module>itop-core-update</module>
|
||||||
|
<module>itop-hub-connector</module>
|
||||||
|
<module>itop-oauth-client</module>
|
||||||
|
<module>combodo-backoffice-darkmoon-theme</module>
|
||||||
|
<module>combodo-backoffice-fullmoon-high-contrast-theme</module>
|
||||||
|
<module>combodo-backoffice-fullmoon-protanopia-deuteranopia-theme</module>
|
||||||
|
<module>combodo-backoffice-fullmoon-tritanopia-theme</module>
|
||||||
|
<module>itop-themes-compat</module>
|
||||||
|
<module>combodo-my-account</module>
|
||||||
|
<module>combodo-my-account-user-info</module>
|
||||||
|
<module>combodo-oauth2-client</module>
|
||||||
|
<module>itop-attribute-class-set</module>
|
||||||
|
<module>itop-attribute-encrypted-password</module>
|
||||||
|
<module>itop-ui-copypaste</module>
|
||||||
|
</modules>
|
||||||
|
<mandatory>true</mandatory>
|
||||||
|
</choice>
|
||||||
|
<choice>
|
||||||
|
<extension_code>itop-config-mgmt-datacenter</extension_code>
|
||||||
|
<title>Data Center Devices</title>
|
||||||
|
<description>Manage Data Center devices such as Racks, Enclosures, PDUs, etc.</description>
|
||||||
|
<modules type="array">
|
||||||
|
<module>itop-datacenter-mgmt</module>
|
||||||
|
</modules>
|
||||||
|
<default>true</default>
|
||||||
|
</choice>
|
||||||
|
<choice>
|
||||||
|
<extension_code>itop-config-mgmt-end-user</extension_code>
|
||||||
|
<title>End-User Devices</title>
|
||||||
|
<description>Manage devices related to end-users: PCs, Phones, Tablets, etc.</description>
|
||||||
|
<modules type="array">
|
||||||
|
<module>itop-endusers-devices</module>
|
||||||
|
</modules>
|
||||||
|
<default>true</default>
|
||||||
|
</choice>
|
||||||
|
<choice>
|
||||||
|
<extension_code>itop-config-mgmt-storage</extension_code>
|
||||||
|
<title>Storage Devices</title>
|
||||||
|
<description>Manage storage devices such as NAS, SAN Switches, Tape Libraries and Tapes, etc.</description>
|
||||||
|
<modules type="array">
|
||||||
|
<module>itop-storage-mgmt</module>
|
||||||
|
</modules>
|
||||||
|
<default>true</default>
|
||||||
|
</choice>
|
||||||
|
<choice>
|
||||||
|
<extension_code>itop-config-mgmt-virtualization</extension_code>
|
||||||
|
<title>Virtualization</title>
|
||||||
|
<description>Manage Hypervisors, Virtual Machines and Farms.</description>
|
||||||
|
<modules type="array">
|
||||||
|
<module>itop-virtualization-mgmt</module>
|
||||||
|
</modules>
|
||||||
|
<default>true</default>
|
||||||
|
<sub_options>
|
||||||
|
<options type="array">
|
||||||
|
<choice>
|
||||||
|
<extension_code>itop-container-mgmt</extension_code>
|
||||||
|
<title>Containerization</title>
|
||||||
|
<description><![CDATA[Manage Container Images, Applications and Hosts]]></description>
|
||||||
|
<modules type="array">
|
||||||
|
<module>itop-container-mgmt</module>
|
||||||
|
</modules>
|
||||||
|
<default>false</default>
|
||||||
|
</choice>
|
||||||
|
</options>
|
||||||
|
</sub_options>
|
||||||
|
</choice>
|
||||||
|
</options>
|
||||||
|
</step>
|
||||||
|
<step>
|
||||||
|
<title>Service Management options</title>
|
||||||
|
<description><![CDATA[<h2>Select the choice that best describes the relationships between the services and the IT infrastructure in your IT environment.</h2>]]></description>
|
||||||
|
<banner>/images/icons/icons8-services.svg</banner>
|
||||||
|
<alternatives type="array">
|
||||||
|
<choice>
|
||||||
|
<extension_code>itop-service-mgmt-enterprise</extension_code>
|
||||||
|
<title>Service Management for Enterprises</title>
|
||||||
|
<description>Select this option if the IT delivers services based on a shared infrastructure. For example if different organizations within your company subscribe to services (like Mail and Print services) delivered by a single shared backend.</description>
|
||||||
|
<modules type="array">
|
||||||
|
<module>itop-service-mgmt</module>
|
||||||
|
</modules>
|
||||||
|
<default>true</default>
|
||||||
|
</choice>
|
||||||
|
<choice>
|
||||||
|
<extension_code>itop-service-mgmt-service-provider</extension_code>
|
||||||
|
<title>Service Management for Service Providers</title>
|
||||||
|
<description>Select this option if the IT manages the infrastructure of independent customers. This is the most flexible model, since the services can be delivered with a mix of shared and customer specific infrastructure devices.</description>
|
||||||
|
<modules type="array">
|
||||||
|
<module>itop-service-mgmt-provider</module>
|
||||||
|
</modules>
|
||||||
|
</choice>
|
||||||
|
</alternatives>
|
||||||
|
</step>
|
||||||
|
<step>
|
||||||
|
<title>Tickets Management options</title>
|
||||||
|
<description><![CDATA[<h2>Select the type of tickets you want to use in order to respond to user requests and incidents.</h2>]]></description>
|
||||||
|
<banner>/images/icons/icons8-discussion-forum.svg</banner>
|
||||||
|
<alternatives type="array">
|
||||||
|
<choice>
|
||||||
|
<extension_code>itop-ticket-mgmt-simple-ticket</extension_code>
|
||||||
|
<title>Simple Ticket Management</title>
|
||||||
|
<description>Select this option to use one single type of tickets for all kind of requests.</description>
|
||||||
|
<modules type="array">
|
||||||
|
<module>itop-request-mgmt</module>
|
||||||
|
</modules>
|
||||||
|
<default>true</default>
|
||||||
|
<sub_options>
|
||||||
|
<options type="array">
|
||||||
|
<choice>
|
||||||
|
<extension_code>itop-ticket-mgmt-simple-ticket-enhanced-portal</extension_code>
|
||||||
|
<title>Customer Portal</title>
|
||||||
|
<description><![CDATA[Modern & responsive portal for the end-users]]></description>
|
||||||
|
<modules type="array">
|
||||||
|
<module>itop-portal</module>
|
||||||
|
<module>itop-portal-base</module>
|
||||||
|
</modules>
|
||||||
|
<default>true</default>
|
||||||
|
</choice>
|
||||||
|
</options>
|
||||||
|
</sub_options>
|
||||||
|
</choice>
|
||||||
|
<choice>
|
||||||
|
<extension_code>itop-ticket-mgmt-itil</extension_code>
|
||||||
|
<title>ITIL Compliant Tickets Management</title>
|
||||||
|
<description>Select this option to have different types of ticket for managing user requests and incidents. Each type of ticket has a specific life cycle and specific fields</description>
|
||||||
|
<sub_options>
|
||||||
|
<options type="array">
|
||||||
|
<choice>
|
||||||
|
<extension_code>itop-ticket-mgmt-itil-user-request</extension_code>
|
||||||
|
<title>User Request Management</title>
|
||||||
|
<description>Manage User Request tickets in iTop</description>
|
||||||
|
<modules type="array">
|
||||||
|
<module>itop-request-mgmt-itil</module>
|
||||||
|
</modules>
|
||||||
|
</choice>
|
||||||
|
<choice>
|
||||||
|
<extension_code>itop-ticket-mgmt-itil-incident</extension_code>
|
||||||
|
<title>Incident Management</title>
|
||||||
|
<description>Manage Incidents tickets in iTop</description>
|
||||||
|
<modules type="array">
|
||||||
|
<module>itop-incident-mgmt-itil</module>
|
||||||
|
</modules>
|
||||||
|
</choice>
|
||||||
|
<choice>
|
||||||
|
<extension_code>itop-ticket-mgmt-itil-enhanced-portal</extension_code>
|
||||||
|
<title>Customer Portal</title>
|
||||||
|
<description><![CDATA[Modern & responsive portal for the end-users]]></description>
|
||||||
|
<modules type="array">
|
||||||
|
<module>itop-portal</module>
|
||||||
|
<module>itop-portal-base</module>
|
||||||
|
</modules>
|
||||||
|
<default>true</default>
|
||||||
|
</choice>
|
||||||
|
</options>
|
||||||
|
</sub_options>
|
||||||
|
</choice>
|
||||||
|
<choice>
|
||||||
|
<extension_code>itop-ticket-mgmt-none</extension_code>
|
||||||
|
<title>No Tickets Management</title>
|
||||||
|
<description>Don't manage incidents or user requests in iTop</description>
|
||||||
|
<modules type="array">
|
||||||
|
</modules>
|
||||||
|
</choice>
|
||||||
|
</alternatives>
|
||||||
|
</step>
|
||||||
|
<step>
|
||||||
|
<title>Change Management options</title>
|
||||||
|
<description><![CDATA[<h2>Select the type of tickets you want to use in order to manage changes to the IT infrastructure.</h2>]]></description>
|
||||||
|
<banner>/images/icons/icons8-change.svg</banner>
|
||||||
|
<alternatives type="array">
|
||||||
|
<choice>
|
||||||
|
<extension_code>itop-change-mgmt-simple</extension_code>
|
||||||
|
<title>Simple Change Management</title>
|
||||||
|
<description>Select this option to use one type of ticket for all kind of changes.</description>
|
||||||
|
<modules type="array">
|
||||||
|
<module>itop-change-mgmt</module>
|
||||||
|
</modules>
|
||||||
|
<default>true</default>
|
||||||
|
</choice>
|
||||||
|
<choice>
|
||||||
|
<extension_code>itop-change-mgmt-itil</extension_code>
|
||||||
|
<title>ITIL Change Management</title>
|
||||||
|
<description>Select this option to use Normal/Routine/Emergency change tickets.</description>
|
||||||
|
<modules type="array">
|
||||||
|
<module>itop-change-mgmt-itil</module>
|
||||||
|
</modules>
|
||||||
|
</choice>
|
||||||
|
<choice>
|
||||||
|
<extension_code>itop-change-mgmt-none</extension_code>
|
||||||
|
<title>No Change Management</title>
|
||||||
|
<description>Don't manage changes in iTop</description>
|
||||||
|
<modules type="array">
|
||||||
|
</modules>
|
||||||
|
</choice>
|
||||||
|
</alternatives>
|
||||||
|
</step>
|
||||||
|
<step>
|
||||||
|
<title>Additional ITIL tickets</title>
|
||||||
|
<description><![CDATA[<h2>Pick from the list below the additional ITIL processes that are to be implemented in iTop.</h2>]]></description>
|
||||||
|
<banner>/images/icons/icons8-important-book.svg</banner>
|
||||||
|
<options type="array">
|
||||||
|
<choice>
|
||||||
|
<!-- Extension code has a typo but fixing it would remove that setup option for all existing iTop -->
|
||||||
|
<extension_code>itop-kown-error-mgmt</extension_code>
|
||||||
|
<title>Known Errors Management and FAQ</title>
|
||||||
|
<description>Select this option to track "Known Errors" and FAQs in iTop.</description>
|
||||||
|
<modules type="array">
|
||||||
|
<module>itop-faq-light</module>
|
||||||
|
<module>itop-knownerror-mgmt</module>
|
||||||
|
</modules>
|
||||||
|
</choice>
|
||||||
|
<choice>
|
||||||
|
<extension_code>itop-problem-mgmt</extension_code>
|
||||||
|
<title>Problem Management</title>
|
||||||
|
<description>Select this option to track "Problems" in iTop.</description>
|
||||||
|
<modules type="array">
|
||||||
|
<module>itop-problem-mgmt</module>
|
||||||
|
</modules>
|
||||||
|
</choice>
|
||||||
|
</options>
|
||||||
|
</step>
|
||||||
|
</steps>
|
||||||
|
</installation>
|
||||||
@@ -0,0 +1,107 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
SetupWebPage::AddModule(
|
||||||
|
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
|
||||||
|
'itop-config-mgmt/3.3.0',
|
||||||
|
[
|
||||||
|
// Identification
|
||||||
|
//
|
||||||
|
'label' => 'Configuration Management (CMDB)',
|
||||||
|
'category' => 'business',
|
||||||
|
|
||||||
|
// Setup
|
||||||
|
//
|
||||||
|
'dependencies' => [
|
||||||
|
'itop-structure/2.7.1',
|
||||||
|
],
|
||||||
|
'mandatory' => false,
|
||||||
|
'visible' => true,
|
||||||
|
'installer' => 'ConfigMgmtInstaller',
|
||||||
|
|
||||||
|
// Components
|
||||||
|
//
|
||||||
|
'datamodel' => [
|
||||||
|
'model.itop-config-mgmt.php',
|
||||||
|
'main.itop-config-mgmt.php',
|
||||||
|
],
|
||||||
|
'data.struct' => [
|
||||||
|
'data/en_us.data.itop-brand.xml',
|
||||||
|
'data/en_us.data.itop-networkdevicetype.xml',
|
||||||
|
'data/en_us.data.itop-osfamily.xml',
|
||||||
|
'data/en_us.data.itop-osversion.xml',
|
||||||
|
],
|
||||||
|
'data.sample' => [
|
||||||
|
'data/data.sample.model.xml',
|
||||||
|
'data/data.sample.networkdevicetype.xml',
|
||||||
|
'data/data.sample.servers.xml',
|
||||||
|
'data/data.sample.nw-devices.xml',
|
||||||
|
'data/data.sample.software.xml',
|
||||||
|
'data/data.sample.dbserver.xml',
|
||||||
|
'data/data.sample.dbschema.xml',
|
||||||
|
'data/data.sample.webserver.xml',
|
||||||
|
'data/data.sample.webapp.xml',
|
||||||
|
'data/data.sample.applications.xml',
|
||||||
|
'data/data.sample.applicationsolutionci.xml',
|
||||||
|
],
|
||||||
|
|
||||||
|
// Documentation
|
||||||
|
//
|
||||||
|
'doc.manual_setup' => '',
|
||||||
|
'doc.more_information' => '',
|
||||||
|
|
||||||
|
// Default settings
|
||||||
|
//
|
||||||
|
'settings' => [
|
||||||
|
],
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!class_exists('ConfigMgmtInstaller')) {
|
||||||
|
// Module installation handler
|
||||||
|
//
|
||||||
|
class ConfigMgmtInstaller extends ModuleInstallerAPI
|
||||||
|
{
|
||||||
|
public static function BeforeWritingConfig(Config $oConfiguration)
|
||||||
|
{
|
||||||
|
// If you want to override/force some configuration values, do it here
|
||||||
|
return $oConfiguration;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler called before creating or upgrading the database schema
|
||||||
|
* @param $oConfiguration Config The new configuration of the application
|
||||||
|
* @param $sPreviousVersion string PRevious version number of the module (empty string in case of first install)
|
||||||
|
* @param $sCurrentVersion string Current version number of the module
|
||||||
|
*/
|
||||||
|
public static function BeforeDatabaseCreation(Config $oConfiguration, $sPreviousVersion, $sCurrentVersion)
|
||||||
|
{
|
||||||
|
if (strlen($sPreviousVersion) > 0) {
|
||||||
|
// If you want to migrate data from one format to another, do it here
|
||||||
|
self::RenameEnumValueInDB('Software', 'type', 'DBserver', 'DBServer');
|
||||||
|
self::RenameEnumValueInDB('Software', 'type', 'Webserver', 'WebServer');
|
||||||
|
self::RenameEnumValueInDB('Model', 'type', 'SANswitch', 'SANSwitch');
|
||||||
|
self::RenameEnumValueInDB('Model', 'type', 'IpPhone', 'IPPhone');
|
||||||
|
self::RenameEnumValueInDB('Model', 'type', 'Telephone', 'Phone');
|
||||||
|
self::RenameClassInDB('DBserver', 'DBServer');
|
||||||
|
self::RenameClassInDB('OSfamily', 'OSFamily');
|
||||||
|
self::RenameClassInDB('OSversion', 'OSVersion');
|
||||||
|
self::RenameClassInDB('Webserver', 'WebServer');
|
||||||
|
self::RenameClassInDB('OSpatch', 'OSPatch');
|
||||||
|
self::RenameClassInDB('lnkFunctionalCIToOSpatch', 'lnkFunctionalCIToOSPatch');
|
||||||
|
self::RenameClassInDB('OsLicence', 'OSLicence');
|
||||||
|
self::RenameClassInDB('IOSversion', 'IOSVersion');
|
||||||
|
self::RenameClassInDB('IPinterface', 'IPInterface');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler called after the creation/update of the database schema
|
||||||
|
* @param $oConfiguration Config The new configuration of the application
|
||||||
|
* @param $sPreviousVersion string PRevious version number of the module (empty string in case of first install)
|
||||||
|
* @param $sCurrentVersion string Current version number of the module
|
||||||
|
*/
|
||||||
|
public static function AfterDatabaseCreation(Config $oConfiguration, $sPreviousVersion, $sCurrentVersion)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
SetupWebPage::AddModule(
|
||||||
|
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
|
||||||
|
'itop-service-mgmt/3.3.0',
|
||||||
|
[
|
||||||
|
// Identification
|
||||||
|
//
|
||||||
|
'label' => 'Service Management',
|
||||||
|
'category' => 'business',
|
||||||
|
|
||||||
|
// Setup
|
||||||
|
//
|
||||||
|
'dependencies' => [
|
||||||
|
'itop-tickets/2.0.0',
|
||||||
|
],
|
||||||
|
'mandatory' => false,
|
||||||
|
'visible' => true,
|
||||||
|
'installer' => 'ServiceMgmtInstaller',
|
||||||
|
|
||||||
|
// Components
|
||||||
|
//
|
||||||
|
'datamodel' => [
|
||||||
|
],
|
||||||
|
'data.struct' => [
|
||||||
|
//'data.struct.itop-service-mgmt.xml',
|
||||||
|
],
|
||||||
|
'data.sample' => [
|
||||||
|
'data/data.sample.organizations.xml',
|
||||||
|
'data/data.sample.contracts.xml',
|
||||||
|
'data/data.sample.servicefamilies.xml',
|
||||||
|
'data/data.sample.services.xml',
|
||||||
|
'data/data.sample.serviceelements.xml',
|
||||||
|
'data/data.sample.sla.xml',
|
||||||
|
'data/data.sample.slt.xml',
|
||||||
|
'data/data.sample.sltsla.xml',
|
||||||
|
// 'data/data.sample.coveragewindows.xml',
|
||||||
|
'data/data.sample.contractservice.xml',
|
||||||
|
// 'data/data.sample.deliverymodel.xml',
|
||||||
|
'data/data.sample.deliverymodelcontact.xml',
|
||||||
|
],
|
||||||
|
|
||||||
|
// Documentation
|
||||||
|
//
|
||||||
|
'doc.manual_setup' => '',
|
||||||
|
'doc.more_information' => '',
|
||||||
|
|
||||||
|
// Default settings
|
||||||
|
//
|
||||||
|
'settings' => [
|
||||||
|
],
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!class_exists('ServiceMgmtInstaller')) {
|
||||||
|
// Module installation handler
|
||||||
|
//
|
||||||
|
class ServiceMgmtInstaller extends ModuleInstallerAPI
|
||||||
|
{
|
||||||
|
public static function BeforeWritingConfig(Config $oConfiguration)
|
||||||
|
{
|
||||||
|
// If you want to override/force some configuration values, do it here
|
||||||
|
return $oConfiguration;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler called before creating or upgrading the database schema
|
||||||
|
* @param $oConfiguration Config The new configuration of the application
|
||||||
|
* @param $sPreviousVersion string PRevious version number of the module (empty string in case of first install)
|
||||||
|
* @param $sCurrentVersion string Current version number of the module
|
||||||
|
*/
|
||||||
|
public static function BeforeDatabaseCreation(Config $oConfiguration, $sPreviousVersion, $sCurrentVersion)
|
||||||
|
{
|
||||||
|
if (strlen($sPreviousVersion) > 0) {
|
||||||
|
self::RenameEnumValueInDB('SLT', 'request_type', 'servicerequest', 'service_request');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler called after the creation/update of the database schema
|
||||||
|
* @param $oConfiguration Config The new configuration of the application
|
||||||
|
* @param $sPreviousVersion string PRevious version number of the module (empty string in case of first install)
|
||||||
|
* @param $sCurrentVersion string Current version number of the module
|
||||||
|
*/
|
||||||
|
public static function AfterDatabaseCreation(Config $oConfiguration, $sPreviousVersion, $sCurrentVersion)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,449 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
SetupWebPage::AddModule(
|
||||||
|
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
|
||||||
|
'itop-structure/3.3.0',
|
||||||
|
[
|
||||||
|
// Identification
|
||||||
|
//
|
||||||
|
'label' => 'Core iTop Structure',
|
||||||
|
'category' => 'business',
|
||||||
|
|
||||||
|
// Setup
|
||||||
|
//
|
||||||
|
'dependencies' => [
|
||||||
|
],
|
||||||
|
'mandatory' => true,
|
||||||
|
'visible' => false,
|
||||||
|
'installer' => 'StructureInstaller',
|
||||||
|
|
||||||
|
// Components
|
||||||
|
//
|
||||||
|
'datamodel' => [
|
||||||
|
'main.itop-structure.php',
|
||||||
|
],
|
||||||
|
'data.struct' => [
|
||||||
|
],
|
||||||
|
'data.sample' => [
|
||||||
|
'data/data.sample.organizations.xml',
|
||||||
|
'data/data.sample.locations.xml',
|
||||||
|
'data/data.sample.persons.xml',
|
||||||
|
'data/data.sample.teams.xml',
|
||||||
|
'data/data.sample.contactteam.xml',
|
||||||
|
'data/data.sample.contacttype.xml',
|
||||||
|
],
|
||||||
|
|
||||||
|
// Documentation
|
||||||
|
//
|
||||||
|
'doc.manual_setup' => '',
|
||||||
|
'doc.more_information' => '',
|
||||||
|
|
||||||
|
// Default settings
|
||||||
|
//
|
||||||
|
'settings' => [
|
||||||
|
],
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!class_exists('StructureInstaller')) {
|
||||||
|
// Module installation handler
|
||||||
|
//
|
||||||
|
class StructureInstaller extends ModuleInstallerAPI
|
||||||
|
{
|
||||||
|
public static function BeforeWritingConfig(Config $oConfiguration)
|
||||||
|
{
|
||||||
|
// If you want to override/force some configuration values, do it here
|
||||||
|
return $oConfiguration;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler called before creating or upgrading the database schema
|
||||||
|
* @param $oConfiguration Config The new configuration of the application
|
||||||
|
* @param $sPreviousVersion string PRevious version number of the module (empty string in case of first install)
|
||||||
|
* @param $sCurrentVersion string Current version number of the module
|
||||||
|
*/
|
||||||
|
public static function BeforeDatabaseCreation(Config $oConfiguration, $sPreviousVersion, $sCurrentVersion)
|
||||||
|
{
|
||||||
|
if (strlen($sPreviousVersion) > 0) {
|
||||||
|
// Search for existing ActionEmail where the language attribute was defined on its child
|
||||||
|
if (version_compare($sPreviousVersion, '3.2.0', '<')) {
|
||||||
|
SetupLog::Info("| Migrate ActionEmail language attribute values to its parent.");
|
||||||
|
$sTableToRead = MetaModel::DBGetTable('ActionEmail');
|
||||||
|
$sTableToSet = MetaModel::DBGetTable('ActionNotification');
|
||||||
|
self::MoveColumnInDB($sTableToRead, 'language', $sTableToSet, 'language', true);
|
||||||
|
SetupLog::Info("| ActionEmail migration done.");
|
||||||
|
}
|
||||||
|
// If you want to migrate data from one format to another, do it here
|
||||||
|
self::RenameEnumValueInDB('Software', 'type', 'DBserver', 'DBServer');
|
||||||
|
self::RenameEnumValueInDB('Software', 'type', 'Webserver', 'WebServer');
|
||||||
|
self::RenameEnumValueInDB('Model', 'type', 'SANswitch', 'SANSwitch');
|
||||||
|
self::RenameEnumValueInDB('Model', 'type', 'IpPhone', 'IPPhone');
|
||||||
|
self::RenameEnumValueInDB('Model', 'type', 'Telephone', 'Phone');
|
||||||
|
self::RenameClassInDB('DBserver', 'DBServer');
|
||||||
|
self::RenameClassInDB('OSfamily', 'OSFamily');
|
||||||
|
self::RenameClassInDB('OSversion', 'OSVersion');
|
||||||
|
self::RenameClassInDB('Webserver', 'WebServer');
|
||||||
|
self::RenameClassInDB('OSpatch', 'OSPatch');
|
||||||
|
self::RenameClassInDB('lnkFunctionalCIToOSpatch', 'lnkFunctionalCIToOSPatch');
|
||||||
|
self::RenameClassInDB('OsLicence', 'OSLicence');
|
||||||
|
self::RenameClassInDB('IOSversion', 'IOSVersion');
|
||||||
|
self::RenameClassInDB('IPinterface', 'IPInterface');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler called after the creation/update of the database schema
|
||||||
|
* @param $oConfiguration Config The new configuration of the application
|
||||||
|
* @param $sPreviousVersion string PRevious version number of the module (empty string in case of first install)
|
||||||
|
* @param $sCurrentVersion string Current version number of the module
|
||||||
|
*/
|
||||||
|
public static function AfterDatabaseCreation(Config $oConfiguration, $sPreviousVersion, $sCurrentVersion)
|
||||||
|
{
|
||||||
|
// Default language will be used for actions
|
||||||
|
// Note: There is a issue when upgrading, default language cannot be retrieved from the passed configuration, we have to read it from the disk
|
||||||
|
if (utils::IsNullOrEmptyString($sPreviousVersion)) {
|
||||||
|
// Fresh install
|
||||||
|
$sDefaultLanguage = $oConfiguration->GetDefaultLanguage();
|
||||||
|
} else {
|
||||||
|
// Upgrade
|
||||||
|
$sDefaultLanguage = utils::GetConfig(true)->GetDefaultLanguage();
|
||||||
|
}
|
||||||
|
// Fallback language on english if not french
|
||||||
|
$sDefaultLanguage = $sDefaultLanguage === 'FR FR' ? 'FR FR' : 'EN US';
|
||||||
|
SetupLog::Info("Default app language used for actions: $sDefaultLanguage");
|
||||||
|
|
||||||
|
// Search for existing TriggerOnObject where the Trigger string complement is empty and fed it with target_class field value
|
||||||
|
if (version_compare($sPreviousVersion, '3.1.0', '<')) {
|
||||||
|
SetupLog::Info("| Feed computed field triggering_class on existing Triggers.");
|
||||||
|
|
||||||
|
$sTableToSet = MetaModel::DBGetTable('Trigger', 'complement');
|
||||||
|
$sTableToRead = MetaModel::DBGetTable('TriggerOnObject', 'target_class');
|
||||||
|
$oAttDefToSet = MetaModel::GetAttributeDef('Trigger', 'complement');
|
||||||
|
$oAttDefToRead = MetaModel::GetAttributeDef('TriggerOnObject', 'target_class');
|
||||||
|
|
||||||
|
$aColumnsToSets = array_keys($oAttDefToSet->GetSQLColumns());
|
||||||
|
$sColumnToSet = $aColumnsToSets[0]; // We know that a string has only one column
|
||||||
|
$aColumnsToReads = array_keys($oAttDefToRead->GetSQLColumns());
|
||||||
|
$sColumnToRead = $aColumnsToReads[0]; // We know that a string has only one column
|
||||||
|
|
||||||
|
$sRepair = "UPDATE $sTableToSet JOIN $sTableToRead ON $sTableToSet.id = $sTableToRead.id SET $sTableToSet.$sColumnToSet = CONCAT('class restriction: ',$sTableToRead.$sColumnToRead) WHERE $sTableToSet.$sColumnToSet = ''";
|
||||||
|
SetupLog::Debug(" | | Query: ".$sRepair);
|
||||||
|
CMDBSource::Query($sRepair);
|
||||||
|
$iNbProcessed = CMDBSource::AffectedRows();
|
||||||
|
SetupLog::Info("| | ".$iNbProcessed." triggers processed.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add notifications by email to Persons if mentioned on any log
|
||||||
|
if (version_compare($sPreviousVersion, '3.0.0', '<')) {
|
||||||
|
SetupLog::Info("Adding default triggers/action for Person objects mentions. All DM classes with at least 1 log attribute will be concerned...");
|
||||||
|
|
||||||
|
$sPersonClass = 'Person';
|
||||||
|
$sPersonStateAttCode = MetaModel::GetStateAttributeCode($sPersonClass);
|
||||||
|
$sPersonOwnerOrgAttCode = UserRightsProfile::GetOwnerOrganizationAttCode($sPersonClass);
|
||||||
|
|
||||||
|
$iClassesWithLogCount = 0;
|
||||||
|
$aCreatedTriggerIds = [];
|
||||||
|
foreach (MetaModel::EnumRootClasses() as $sRootClass) {
|
||||||
|
foreach (MetaModel::EnumChildClasses($sRootClass, ENUM_CHILD_CLASSES_ALL, true) as $sClass) {
|
||||||
|
$aLogAttCodes = MetaModel::GetAttributesList($sClass, ['AttributeCaseLog']);
|
||||||
|
|
||||||
|
// Skip class with no log attribute
|
||||||
|
if (count($aLogAttCodes) === 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prepare the mentioned_filter OQL
|
||||||
|
$oPersonSearch = DBObjectSearch::FromOQL("SELECT $sPersonClass");
|
||||||
|
|
||||||
|
// - Add status condition if attribute present
|
||||||
|
if (empty($sPersonStateAttCode) === false) {
|
||||||
|
$oPersonSearch->AddConditionExpression(new BinaryExpression(
|
||||||
|
new FieldExpression($sPersonStateAttCode),
|
||||||
|
'=',
|
||||||
|
new ScalarExpression('active')
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
// - Check if the classes have a silo attribute so we can use them in the mentioned_filter
|
||||||
|
if (empty($sPersonOwnerOrgAttCode) === false) {
|
||||||
|
// Filter on current contact org.
|
||||||
|
$oCurrentContactExpr = new BinaryExpression(
|
||||||
|
new FieldExpression($sPersonOwnerOrgAttCode),
|
||||||
|
'=',
|
||||||
|
new VariableExpression("current_contact->org_id")
|
||||||
|
);
|
||||||
|
|
||||||
|
// Filter on class owner org. if any
|
||||||
|
$sClassOwnerOrgAttCode = UserRightsProfile::GetOwnerOrganizationAttCode($sClass);
|
||||||
|
$oOwnerOrgExpr = empty($sClassOwnerOrgAttCode) ? null : new BinaryExpression(
|
||||||
|
new FieldExpression($sPersonOwnerOrgAttCode),
|
||||||
|
'=',
|
||||||
|
new VariableExpression("this->$sClassOwnerOrgAttCode")
|
||||||
|
);
|
||||||
|
|
||||||
|
// No owner org, simple condition
|
||||||
|
if ($oOwnerOrgExpr === null) {
|
||||||
|
$oPersonSearch->AddConditionExpression($oCurrentContactExpr);
|
||||||
|
}
|
||||||
|
// Owner org, condition is either from owner org or current contact's
|
||||||
|
else {
|
||||||
|
$oOrExpr = new BinaryExpression($oCurrentContactExpr, 'OR', $oOwnerOrgExpr);
|
||||||
|
$oPersonSearch->AddConditionExpression($oOrExpr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build the trigger
|
||||||
|
$oTrigger = MetaModel::NewObject(TriggerOnObjectMention::class);
|
||||||
|
$oTrigger->Set('description', 'Person mentioned on '.$sClass);
|
||||||
|
$oTrigger->Set('target_class', $sClass);
|
||||||
|
$oTrigger->Set('mentioned_filter', $oPersonSearch->ToOQL());
|
||||||
|
$oTrigger->DBInsert();
|
||||||
|
|
||||||
|
SetupLog::Info("|- Created trigger \"{$oTrigger->Get('description')}\" for class $sClass.");
|
||||||
|
$aCreatedTriggerIds[] = $oTrigger->GetKey();
|
||||||
|
$iClassesWithLogCount++;
|
||||||
|
// Note: We break because we only have to create one trigger/action for the class hierarchy as it will be for all their log attributes
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build the corresponding action and link it to the triggers
|
||||||
|
if (count($aCreatedTriggerIds) > 0) {
|
||||||
|
// Actions data for english and french
|
||||||
|
$aActionsData = [
|
||||||
|
'EN US' => [
|
||||||
|
'name' => 'Notification to persons mentioned in logs',
|
||||||
|
'subject' => 'You have been mentioned in "$this->friendlyname$"',
|
||||||
|
'body' => '<p>Hello $mentioned->first_name$,</p>
|
||||||
|
<p>You have been mentioned by $current_contact->friendlyname$ in $this->hyperlink()$</p>',
|
||||||
|
],
|
||||||
|
'FR FR' => [
|
||||||
|
'name' => 'Notification aux personnes mentionnées dans les journaux',
|
||||||
|
'subject' => 'Vous avez été mentionné dans "$this->friendlyname$"',
|
||||||
|
'body' => '<p>Bonjour $mentioned->first_name$,</p>
|
||||||
|
<p>Vous avez été mentionné par $current_contact->friendlyname$ dans $this->hyperlink()$</p>',
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
// Create action in app. default language and link it to the triggers
|
||||||
|
$aData = $aActionsData[$sDefaultLanguage];
|
||||||
|
$oAction = MetaModel::NewObject(ActionEmail::class);
|
||||||
|
$oAction->Set('name', $aData['name']);
|
||||||
|
$oAction->Set('status', 'enabled');
|
||||||
|
$oAction->Set('language', $sDefaultLanguage);
|
||||||
|
$oAction->Set('from', '$current_contact->email$');
|
||||||
|
$oAction->Set('to', 'SELECT Person WHERE id = :mentioned->id');
|
||||||
|
$oAction->Set('subject', $aData['subject']);
|
||||||
|
$oAction->Set('body', $aData['body']);
|
||||||
|
|
||||||
|
/** @var \ormLinkSet $oOrm */
|
||||||
|
$oOrm = $oAction->Get('trigger_list');
|
||||||
|
foreach ($aCreatedTriggerIds as $sTriggerId) {
|
||||||
|
$oLink = new lnkTriggerAction();
|
||||||
|
$oLink->Set('trigger_id', $sTriggerId);
|
||||||
|
$oOrm->AddItem($oLink);
|
||||||
|
}
|
||||||
|
$oAction->Set('trigger_list', $oOrm);
|
||||||
|
$oAction->DBInsert();
|
||||||
|
|
||||||
|
SetupLog::Info("|- Created action \"{$oAction->Get('name')}\" and linked it to the previously created triggers.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($iClassesWithLogCount === 0) {
|
||||||
|
SetupLog::Info("... no trigger/action created as there is no DM class with a log attribute.");
|
||||||
|
} else {
|
||||||
|
SetupLog::Info("... default triggers/action successfully created for $iClassesWithLogCount classes.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add notifications by newsroom to Persons if mentioned on any log
|
||||||
|
if (version_compare($sPreviousVersion, '3.2.0', '<')) {
|
||||||
|
SetupLog::Info("Adding default newsroom actions for Person objects mentions. All existing TriggerOnObjectMention mentioning the Person class will be concerned...");
|
||||||
|
|
||||||
|
$sPersonClass = Person::class;
|
||||||
|
$iExistingTriggersCount = 0;
|
||||||
|
|
||||||
|
// Actions data for english and french
|
||||||
|
$aActionsData = [
|
||||||
|
'EN US' => [
|
||||||
|
'name' => 'Notification to persons mentioned in logs',
|
||||||
|
'message' => 'You have been mentioned by $current_contact->friendlyname$',
|
||||||
|
],
|
||||||
|
'FR FR' => [
|
||||||
|
'name' => 'Notification aux personnes mentionnées dans les journaux',
|
||||||
|
'message' => 'Vous avez été mentionné par $current_contact->friendlyname$',
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
// Start by creating the default action no matter what (even if there is no relevant trigger, it will be there for future use)
|
||||||
|
$aData = $aActionsData[$sDefaultLanguage];
|
||||||
|
$oAction = MetaModel::NewObject(ActionNewsroom::class);
|
||||||
|
$oAction->Set('name', $aData['name']);
|
||||||
|
$oAction->Set('status', 'enabled');
|
||||||
|
$oAction->Set('language', $sDefaultLanguage);
|
||||||
|
$oAction->Set('priority', 3); // Important priority as a mention is probably more important than a simple notification
|
||||||
|
$oAction->Set('recipients', 'SELECT Person WHERE id = :mentioned->id');
|
||||||
|
$oAction->Set('title', '$this->friendlyname$');
|
||||||
|
$oAction->Set('message', $aData['message']);
|
||||||
|
$oAction->DBWrite();
|
||||||
|
|
||||||
|
SetupLog::Info("|- Created newsroom action \"{$oAction->Get('name')}\".");
|
||||||
|
|
||||||
|
// Retrieve all triggers and find those with a mentioned_filter on the Person class
|
||||||
|
$oTriggersSearch = DBObjectSearch::FromOQL("SELECT ".TriggerOnObjectMention::class);
|
||||||
|
$oTriggersSearch->AllowAllData();
|
||||||
|
|
||||||
|
$oTriggersSet = new DBObjectSet($oTriggersSearch);
|
||||||
|
while ($oTrigger = $oTriggersSet->Fetch()) {
|
||||||
|
// If mentioned class is not a Person, ignore
|
||||||
|
$oMentionedFilter = DBSearch::FromOQL($oTrigger->Get('mentioned_filter'));
|
||||||
|
if (!is_null($oMentionedFilter) && is_a($oMentionedFilter->GetClass(), $sPersonClass, true) === false) {
|
||||||
|
SetupLog::Info("|- Action \"{$oAction->GetName()}\" NOT LINKED to existing trigger \"{$oTrigger->GetName()}\". (mentioned class \"{$oMentionedFilter->GetClass()}\")");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Link the trigger to the action
|
||||||
|
/** @var \ormLinkSet $oOrm */
|
||||||
|
$oOrm = $oTrigger->Get('action_list');
|
||||||
|
$oLink = new lnkTriggerAction();
|
||||||
|
$oLink->Set('action_id', $oAction->GetKey());
|
||||||
|
$oOrm->AddItem($oLink);
|
||||||
|
|
||||||
|
$oTrigger->Set('action_list', $oOrm);
|
||||||
|
$oTrigger->DBUpdate();
|
||||||
|
$iExistingTriggersCount++;
|
||||||
|
|
||||||
|
SetupLog::Info("|- Linked newsroom action \"{$oAction->GetName()}\" to existing trigger \"{$oTrigger->GetName()}\".");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($iExistingTriggersCount === 0) {
|
||||||
|
SetupLog::Info("... no action created as there is no existing trigger on mention for the $sPersonClass class.");
|
||||||
|
} else {
|
||||||
|
SetupLog::Info("... default newsroom action successfully created and linked to $iExistingTriggersCount triggers on mention.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Force subscription policy to ForceAtLeastOneChannel for all existing TriggerOnObjectMention
|
||||||
|
if (version_compare($sPreviousVersion, '3.2.0', '<')) {
|
||||||
|
SetupLog::Info("Forcing subscription policy to ForceAtLeastOneChannel for all existing TriggerOnObjectMention...");
|
||||||
|
|
||||||
|
$oTriggersSearch = DBObjectSearch::FromOQL("SELECT ".TriggerOnObjectMention::class);
|
||||||
|
$oTriggersSearch->AllowAllData();
|
||||||
|
|
||||||
|
$oTriggersSet = new DBObjectSet($oTriggersSearch);
|
||||||
|
while ($oTrigger = $oTriggersSet->Fetch()) {
|
||||||
|
$oTrigger->Set('subscription_policy', \Combodo\iTop\Core\Trigger\Enum\SubscriptionPolicy::ForceAtLeastOneChannel->value);
|
||||||
|
$oTrigger->DBUpdate();
|
||||||
|
|
||||||
|
SetupLog::Info("|- Trigger \"{$oTrigger->GetName()}\" updated.");
|
||||||
|
}
|
||||||
|
|
||||||
|
SetupLog::Info("... all existing TriggerOnObjectMention updated.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add notifications by newsroom (not linked to any trigger yet) for TriggerOnPortalUpdate and TriggerOnReachingState
|
||||||
|
if (version_compare($sPreviousVersion, '3.2.0', '<')) {
|
||||||
|
// TriggerOnPortalUpdate
|
||||||
|
SetupLog::Info("Adding default newsroom action for TriggerOnPortalUpdate (not linked to any trigger yet)...");
|
||||||
|
|
||||||
|
// - Actions data for english and french
|
||||||
|
$aActionsData = [
|
||||||
|
'EN US' => [
|
||||||
|
'name' => 'Notification on public log update through the portal',
|
||||||
|
'message' => 'New message from $current_contact->friendlyname$',
|
||||||
|
],
|
||||||
|
'FR FR' => [
|
||||||
|
'name' => 'Notification sur MAJ du journal public via le portail',
|
||||||
|
'message' => 'Nouveau message de $current_contact->friendlyname$',
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
// - Create action in app. default language and link it to the triggers
|
||||||
|
$aData = $aActionsData[$sDefaultLanguage];
|
||||||
|
$oAction = MetaModel::NewObject(ActionNewsroom::class);
|
||||||
|
$oAction->Set('name', $aData['name']);
|
||||||
|
$oAction->Set('status', 'enabled');
|
||||||
|
$oAction->Set('language', $sDefaultLanguage);
|
||||||
|
$oAction->Set('priority', 4); // Standard priority
|
||||||
|
$oAction->Set('recipients', 'SELECT Person WHERE id = :this->agent_id');
|
||||||
|
$oAction->Set('title', '$this->friendlyname$');
|
||||||
|
$oAction->Set('message', $aData['message']);
|
||||||
|
$oAction->DBWrite();
|
||||||
|
|
||||||
|
// TriggerOnReachingState
|
||||||
|
SetupLog::Info("Adding default newsroom action for TriggerOnReachingState (not linked to any trigger yet)...");
|
||||||
|
|
||||||
|
// Actions data for english and french
|
||||||
|
$aActionsData = [
|
||||||
|
'EN US' => [
|
||||||
|
'name' => 'Notification to agent when ticket assigned',
|
||||||
|
'message' => 'Ticket has been assigned to you',
|
||||||
|
],
|
||||||
|
'FR FR' => [
|
||||||
|
'name' => 'Notification à l\'agent à l\'assignation du ticket',
|
||||||
|
'message' => 'Le ticket vous a été assigné',
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
// Create action in app. default language and link it to the triggers
|
||||||
|
$aData = $aActionsData[$sDefaultLanguage];
|
||||||
|
$oAction = MetaModel::NewObject(ActionNewsroom::class);
|
||||||
|
$oAction->Set('name', $aData['name']);
|
||||||
|
$oAction->Set('status', 'enabled');
|
||||||
|
$oAction->Set('language', $sDefaultLanguage);
|
||||||
|
$oAction->Set('priority', 3); // Important priority
|
||||||
|
$oAction->Set('recipients', 'SELECT Person WHERE id = :this->agent_id');
|
||||||
|
$oAction->Set('title', '$this->friendlyname$');
|
||||||
|
$oAction->Set('message', $aData['message']);
|
||||||
|
$oAction->DBWrite();
|
||||||
|
}
|
||||||
|
|
||||||
|
//N°824 - Fill object_class in EventNotification from the Triggers target_class
|
||||||
|
if (version_compare($sPreviousVersion, '3.2.0', '<')) {
|
||||||
|
SetupLog::Info("Filling object_class in EventNotification from the Triggers target_class");
|
||||||
|
$iNbProcessed = 0;
|
||||||
|
|
||||||
|
$sTableToSet = MetaModel::DBGetTable('EventNotification', 'object_class');
|
||||||
|
$oAttDefToSet = MetaModel::GetAttributeDef('EventNotification', 'object_class');
|
||||||
|
$oAttDefObjectId = MetaModel::GetAttributeDef('EventNotification', 'object_id');
|
||||||
|
$oAttDefTriggerId = MetaModel::GetAttributeDef('EventNotification', 'trigger_id');
|
||||||
|
|
||||||
|
$aColumnsToSets = array_keys($oAttDefToSet->GetSQLColumns());
|
||||||
|
$sColumnToSet = $aColumnsToSets[0]; // We know that a string has only one column
|
||||||
|
$aColumnsTriggerId = array_keys($oAttDefTriggerId->GetSQLColumns());
|
||||||
|
$sColumnTriggerId = $aColumnsTriggerId[0]; // We know that a string has only one column
|
||||||
|
$aColumnsObjectd = array_keys($oAttDefObjectId->GetSQLColumns());
|
||||||
|
$sColumnObjectId = $aColumnsObjectd[0]; // We know that a string has only one column
|
||||||
|
|
||||||
|
$oSearch = DBObjectSearch::FromOQL('SELECT TriggerOnObject');
|
||||||
|
$oSet = new DBObjectSet($oSearch);
|
||||||
|
$aTriggerIdToTargetClass = [];
|
||||||
|
while ($oTrigger = $oSet->Fetch()) {
|
||||||
|
$aTriggerIdToTargetClass[$oTrigger->GetKey()] = $oTrigger->Get('target_class');
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($aTriggerIdToTargetClass as $sKey => $sTargetClass) {
|
||||||
|
|
||||||
|
if (MetaModel::HasChildrenClasses($sTargetClass)) {
|
||||||
|
//in this case, we have toget the name of the final class
|
||||||
|
$sTableToRead = MetaModel::DBGetTable($sTargetClass, 'finalclass');
|
||||||
|
$oAttDefToRead = MetaModel::GetAttributeDef($sTargetClass, 'finalclass');
|
||||||
|
$aColumnsToReads = array_keys($oAttDefToRead->GetSQLColumns());
|
||||||
|
$sColumnToRead = $aColumnsToReads[0]; // We know that a string has only one column
|
||||||
|
$sObjectPrimaryKey = MetaModel::DBGetKey($sTargetClass);
|
||||||
|
|
||||||
|
$sRepair = "UPDATE `$sTableToSet` JOIN `$sTableToRead` ON `$sTableToSet`.`$sColumnObjectId` = `$sTableToRead`.`$sObjectPrimaryKey` SET `$sTableToSet`.`$sColumnToSet` = `$sTableToRead`.`$sColumnToRead` WHERE `$sTableToSet`.`$sColumnTriggerId` = '".$sKey."' AND `$sTableToSet`.`$sColumnToSet` = ''";
|
||||||
|
} else {
|
||||||
|
|
||||||
|
$sRepair = "UPDATE `$sTableToSet` SET `$sTableToSet`.`$sColumnToSet` = '".$sTargetClass."' WHERE `$sTableToSet`.`$sColumnTriggerId` = '".$sKey."' AND `$sTableToSet`.`$sColumnToSet` = ''";
|
||||||
|
}
|
||||||
|
|
||||||
|
SetupLog::Info(" | | Query: ".$sRepair);
|
||||||
|
CMDBSource::Query($sRepair);
|
||||||
|
$iNbProcessed += CMDBSource::AffectedRows();
|
||||||
|
}
|
||||||
|
SetupLog::Info("| | ".$iNbProcessed." EventNotification processed.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,243 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<installation>
|
||||||
|
<steps type="array">
|
||||||
|
<step>
|
||||||
|
<title>Configuration Management options</title>
|
||||||
|
<description><![CDATA[<h2>The options below allow you to configure the type of elements that are to be managed inside iTop.</h2>]]></description>
|
||||||
|
<banner>/images/icons/icons8-apps-tab.svg</banner>
|
||||||
|
<options type="array">
|
||||||
|
<choice>
|
||||||
|
<extension_code>itop-config-mgmt-core</extension_code>
|
||||||
|
<title>Configuration Management Core</title>
|
||||||
|
<description>All the base objects that are mandatory in the iTop CMDB: Organizations, Locations, Teams, Persons, etc.</description>
|
||||||
|
<modules type="array">
|
||||||
|
<module>itop-structure</module>
|
||||||
|
<module>itop-config-mgmt</module>
|
||||||
|
<module>itop-attachments</module>
|
||||||
|
<module>itop-profiles-itil</module>
|
||||||
|
<module>itop-welcome-itil</module>
|
||||||
|
<module>itop-tickets</module>
|
||||||
|
<module>itop-files-information</module>
|
||||||
|
<module>combodo-db-tools</module>
|
||||||
|
<module>itop-core-update</module>
|
||||||
|
<module>itop-hub-connector</module>
|
||||||
|
<module>itop-oauth-client</module>
|
||||||
|
<module>combodo-backoffice-darkmoon-theme</module>
|
||||||
|
<module>combodo-backoffice-fullmoon-high-contrast-theme</module>
|
||||||
|
<module>combodo-backoffice-fullmoon-protanopia-deuteranopia-theme</module>
|
||||||
|
<module>combodo-backoffice-fullmoon-tritanopia-theme</module>
|
||||||
|
<module>itop-themes-compat</module>
|
||||||
|
<module>combodo-my-account</module>
|
||||||
|
<module>combodo-my-account-user-info</module>
|
||||||
|
<module>combodo-oauth2-client</module>
|
||||||
|
<module>itop-attribute-class-set</module>
|
||||||
|
<module>itop-attribute-encrypted-password</module>
|
||||||
|
<module>itop-ui-copypaste</module>
|
||||||
|
</modules>
|
||||||
|
<mandatory>true</mandatory>
|
||||||
|
</choice>
|
||||||
|
<choice>
|
||||||
|
<extension_code>itop-config-mgmt-datacenter</extension_code>
|
||||||
|
<title>Data Center Devices</title>
|
||||||
|
<description>Manage Data Center devices such as Racks, Enclosures, PDUs, etc.</description>
|
||||||
|
<modules type="array">
|
||||||
|
<module>itop-datacenter-mgmt</module>
|
||||||
|
</modules>
|
||||||
|
<default>true</default>
|
||||||
|
</choice>
|
||||||
|
<choice>
|
||||||
|
<extension_code>itop-config-mgmt-end-user</extension_code>
|
||||||
|
<title>End-User Devices</title>
|
||||||
|
<description>Manage devices related to end-users: PCs, Phones, Tablets, etc.</description>
|
||||||
|
<modules type="array">
|
||||||
|
<module>itop-endusers-devices</module>
|
||||||
|
</modules>
|
||||||
|
<default>true</default>
|
||||||
|
</choice>
|
||||||
|
<choice>
|
||||||
|
<extension_code>itop-config-mgmt-storage</extension_code>
|
||||||
|
<title>Storage Devices</title>
|
||||||
|
<description>Manage storage devices such as NAS, SAN Switches, Tape Libraries and Tapes, etc.</description>
|
||||||
|
<modules type="array">
|
||||||
|
<module>itop-storage-mgmt</module>
|
||||||
|
</modules>
|
||||||
|
<default>true</default>
|
||||||
|
</choice>
|
||||||
|
<choice>
|
||||||
|
<extension_code>itop-config-mgmt-virtualization</extension_code>
|
||||||
|
<title>Virtualization</title>
|
||||||
|
<description>Manage Hypervisors, Virtual Machines and Farms.</description>
|
||||||
|
<modules type="array">
|
||||||
|
<module>itop-virtualization-mgmt</module>
|
||||||
|
</modules>
|
||||||
|
<default>true</default>
|
||||||
|
<sub_options>
|
||||||
|
<options type="array">
|
||||||
|
<choice>
|
||||||
|
<extension_code>itop-container-mgmt</extension_code>
|
||||||
|
<title>Containerization</title>
|
||||||
|
<description><![CDATA[Manage Container Images, Applications and Hosts]]></description>
|
||||||
|
<modules type="array">
|
||||||
|
<module>itop-container-mgmt</module>
|
||||||
|
</modules>
|
||||||
|
<default>false</default>
|
||||||
|
</choice>
|
||||||
|
</options>
|
||||||
|
</sub_options>
|
||||||
|
</choice>
|
||||||
|
</options>
|
||||||
|
</step>
|
||||||
|
<step>
|
||||||
|
<title>Service Management options</title>
|
||||||
|
<description><![CDATA[<h2>Select the choice that best describes the relationships between the services and the IT infrastructure in your IT environment.</h2>]]></description>
|
||||||
|
<banner>/images/icons/icons8-services.svg</banner>
|
||||||
|
<alternatives type="array">
|
||||||
|
<choice>
|
||||||
|
<extension_code>itop-service-mgmt-enterprise</extension_code>
|
||||||
|
<title>Service Management for Enterprises</title>
|
||||||
|
<description>Select this option if the IT delivers services based on a shared infrastructure. For example if different organizations within your company subscribe to services (like Mail and Print services) delivered by a single shared backend.</description>
|
||||||
|
<modules type="array">
|
||||||
|
<module>itop-service-mgmt</module>
|
||||||
|
</modules>
|
||||||
|
<default>true</default>
|
||||||
|
</choice>
|
||||||
|
<choice>
|
||||||
|
<extension_code>itop-service-mgmt-service-provider</extension_code>
|
||||||
|
<title>Service Management for Service Providers</title>
|
||||||
|
<description>Select this option if the IT manages the infrastructure of independent customers. This is the most flexible model, since the services can be delivered with a mix of shared and customer specific infrastructure devices.</description>
|
||||||
|
<modules type="array">
|
||||||
|
<module>itop-service-mgmt-provider</module>
|
||||||
|
</modules>
|
||||||
|
</choice>
|
||||||
|
</alternatives>
|
||||||
|
</step>
|
||||||
|
<step>
|
||||||
|
<title>Tickets Management options</title>
|
||||||
|
<description><![CDATA[<h2>Select the type of tickets you want to use in order to respond to user requests and incidents.</h2>]]></description>
|
||||||
|
<banner>/images/icons/icons8-discussion-forum.svg</banner>
|
||||||
|
<alternatives type="array">
|
||||||
|
<choice>
|
||||||
|
<extension_code>itop-ticket-mgmt-simple-ticket</extension_code>
|
||||||
|
<title>Simple Ticket Management</title>
|
||||||
|
<description>Select this option to use one single type of tickets for all kind of requests.</description>
|
||||||
|
<modules type="array">
|
||||||
|
<module>itop-request-mgmt</module>
|
||||||
|
</modules>
|
||||||
|
<default>true</default>
|
||||||
|
<sub_options>
|
||||||
|
<options type="array">
|
||||||
|
<choice>
|
||||||
|
<extension_code>itop-ticket-mgmt-simple-ticket-enhanced-portal</extension_code>
|
||||||
|
<title>Customer Portal</title>
|
||||||
|
<description><![CDATA[Modern & responsive portal for the end-users]]></description>
|
||||||
|
<modules type="array">
|
||||||
|
<module>itop-portal</module>
|
||||||
|
<module>itop-portal-base</module>
|
||||||
|
</modules>
|
||||||
|
<default>true</default>
|
||||||
|
</choice>
|
||||||
|
</options>
|
||||||
|
</sub_options>
|
||||||
|
</choice>
|
||||||
|
<choice>
|
||||||
|
<extension_code>itop-ticket-mgmt-itil</extension_code>
|
||||||
|
<title>ITIL Compliant Tickets Management</title>
|
||||||
|
<description>Select this option to have different types of ticket for managing user requests and incidents. Each type of ticket has a specific life cycle and specific fields</description>
|
||||||
|
<sub_options>
|
||||||
|
<options type="array">
|
||||||
|
<choice>
|
||||||
|
<extension_code>itop-ticket-mgmt-itil-user-request</extension_code>
|
||||||
|
<title>User Request Management</title>
|
||||||
|
<description>Manage User Request tickets in iTop</description>
|
||||||
|
<modules type="array">
|
||||||
|
<module>itop-request-mgmt-itil</module>
|
||||||
|
</modules>
|
||||||
|
</choice>
|
||||||
|
<choice>
|
||||||
|
<extension_code>itop-ticket-mgmt-itil-incident</extension_code>
|
||||||
|
<title>Incident Management</title>
|
||||||
|
<description>Manage Incidents tickets in iTop</description>
|
||||||
|
<modules type="array">
|
||||||
|
<module>itop-incident-mgmt-itil</module>
|
||||||
|
</modules>
|
||||||
|
</choice>
|
||||||
|
<choice>
|
||||||
|
<extension_code>itop-ticket-mgmt-itil-enhanced-portal</extension_code>
|
||||||
|
<title>Customer Portal</title>
|
||||||
|
<description><![CDATA[Modern & responsive portal for the end-users]]></description>
|
||||||
|
<modules type="array">
|
||||||
|
<module>itop-portal</module>
|
||||||
|
<module>itop-portal-base</module>
|
||||||
|
</modules>
|
||||||
|
<default>true</default>
|
||||||
|
</choice>
|
||||||
|
</options>
|
||||||
|
</sub_options>
|
||||||
|
</choice>
|
||||||
|
<choice>
|
||||||
|
<extension_code>itop-ticket-mgmt-none</extension_code>
|
||||||
|
<title>No Tickets Management</title>
|
||||||
|
<description>Don't manage incidents or user requests in iTop</description>
|
||||||
|
<modules type="array">
|
||||||
|
</modules>
|
||||||
|
</choice>
|
||||||
|
</alternatives>
|
||||||
|
</step>
|
||||||
|
<step>
|
||||||
|
<title>Change Management options</title>
|
||||||
|
<description><![CDATA[<h2>Select the type of tickets you want to use in order to manage changes to the IT infrastructure.</h2>]]></description>
|
||||||
|
<banner>/images/icons/icons8-change.svg</banner>
|
||||||
|
<alternatives type="array">
|
||||||
|
<choice>
|
||||||
|
<extension_code>itop-change-mgmt-simple</extension_code>
|
||||||
|
<title>Simple Change Management</title>
|
||||||
|
<description>Select this option to use one type of ticket for all kind of changes.</description>
|
||||||
|
<modules type="array">
|
||||||
|
<module>itop-change-mgmt</module>
|
||||||
|
</modules>
|
||||||
|
<default>true</default>
|
||||||
|
</choice>
|
||||||
|
<choice>
|
||||||
|
<extension_code>itop-change-mgmt-itil</extension_code>
|
||||||
|
<title>ITIL Change Management</title>
|
||||||
|
<description>Select this option to use Normal/Routine/Emergency change tickets.</description>
|
||||||
|
<modules type="array">
|
||||||
|
<module>itop-change-mgmt-itil</module>
|
||||||
|
</modules>
|
||||||
|
</choice>
|
||||||
|
<choice>
|
||||||
|
<extension_code>itop-change-mgmt-none</extension_code>
|
||||||
|
<title>No Change Management</title>
|
||||||
|
<description>Don't manage changes in iTop</description>
|
||||||
|
<modules type="array">
|
||||||
|
</modules>
|
||||||
|
</choice>
|
||||||
|
</alternatives>
|
||||||
|
</step>
|
||||||
|
<step>
|
||||||
|
<title>Additional ITIL tickets</title>
|
||||||
|
<description><![CDATA[<h2>Pick from the list below the additional ITIL processes that are to be implemented in iTop.</h2>]]></description>
|
||||||
|
<banner>/images/icons/icons8-important-book.svg</banner>
|
||||||
|
<options type="array">
|
||||||
|
<choice>
|
||||||
|
<!-- Extension code has a typo but fixing it would remove that setup option for all existing iTop -->
|
||||||
|
<extension_code>itop-kown-error-mgmt</extension_code>
|
||||||
|
<title>Known Errors Management and FAQ</title>
|
||||||
|
<description>Select this option to track "Known Errors" and FAQs in iTop.</description>
|
||||||
|
<modules type="array">
|
||||||
|
<module>itop-faq-light</module>
|
||||||
|
<module>itop-knownerror-mgmt</module>
|
||||||
|
</modules>
|
||||||
|
</choice>
|
||||||
|
<choice>
|
||||||
|
<extension_code>itop-problem-mgmt</extension_code>
|
||||||
|
<title>Problem Management</title>
|
||||||
|
<description>Select this option to track "Problems" in iTop.</description>
|
||||||
|
<modules type="array">
|
||||||
|
<module>itop-problem-mgmt</module>
|
||||||
|
</modules>
|
||||||
|
</choice>
|
||||||
|
</options>
|
||||||
|
</step>
|
||||||
|
</steps>
|
||||||
|
</installation>
|
||||||
Reference in New Issue
Block a user