Delete all parent classes objects + cleanup

This commit is contained in:
Eric Espie
2026-03-09 14:42:31 +01:00
parent be3ef66f03
commit e71e686509
14 changed files with 125 additions and 56 deletions

View File

@@ -16,9 +16,9 @@ Dict::Add('EN US', 'English', 'English', [
'DataFeatureRemoval:Main:Title' => 'Features Removal',
'DataFeatureRemoval:Main:SubTitle' => 'Prepare features you want to enable/disable in a future setup',
'DataFeatureRemoval:Failure:Title' => 'Feature dry removal errors',
'DataFeatureRemoval:Helper:Title' => 'This utilitary allows you to enable or disable features that are installed in your iTop.',
'DataFeatureRemoval:Helper:Title' => 'Enable or disable features that are installed in your iTop.',
'DataFeatureRemoval:Helper:Desc1' => 'It will prepare the setup step that proceeds to feature enabling or disabling.',
'DataFeatureRemoval:Helper:Desc2' => 'You will need to 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:Analysis:Title' => 'Analysis result',
@@ -27,15 +27,18 @@ Dict::Add('EN US', 'English', 'English', [
'DataFeatureRemoval:Table:Analysis:ClassName' => 'Element to remove',
'DataFeatureRemoval:Table:Analysis:RemovalType' => 'Type of element',
'DataFeatureRemoval:Table:Analysis:FeatureName' => 'Feature name',
'DataFeatureRemoval:Table:Analysis:Occurence' => 'Occurence',
'DataFeatureRemoval:Table:Analysis:Occurrence' => 'Occurrence',
'UI:Button:Analyze' => 'Analyze',
'UI:Button:ModifyChoices' => 'Modify Choices',
'UI:Button:AnalyzeAndSetup' => 'Analyze and go to setup',
'UI:Button:PlanDeletion' => 'Prepare deletion plan',
'UI:Button:DoDeletion' => 'Delete data',
'UI:Button:BackToMain' => 'Back to Feature Removal',
'UI:Action:ForceUninstall' => 'Force uninstall',
'UI:Action:MoreInfo' => 'More information',
'DataFeatureRemoval:Table:Analysis:RemovalType:FINAL_CLASS' => 'Final class',
'DataFeatureRemoval:Table:Empty' => 'No data to remove',
]);

View File

@@ -16,9 +16,9 @@ Dict::Add('FR FR', 'French', 'Français', [
'DataFeatureRemoval:Main:Title' => 'Features Removal',
'DataFeatureRemoval:Main:SubTitle' => 'Prepare features you want to enable/disable in a future setup',
'DataFeatureRemoval:Failure:Title' => 'Feature dry removal errors',
'DataFeatureRemoval:Helper:Title' => 'This utilitary allows you to enable or disable features that are installed in your iTop.',
'DataFeatureRemoval:Helper:Title' => 'Enable or disable features that are installed in your iTop.',
'DataFeatureRemoval:Helper:Desc1' => 'It will prepare the setup step that proceeds to feature enabling or disabling.',
'DataFeatureRemoval:Helper:Desc2' => 'You will need to 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:Analysis:Title' => 'Analysis result',
@@ -27,14 +27,18 @@ Dict::Add('FR FR', 'French', 'Français', [
'DataFeatureRemoval:Table:Analysis:ClassName' => 'Element to remove',
'DataFeatureRemoval:Table:Analysis:RemovalType' => 'Type of element',
'DataFeatureRemoval:Table:Analysis:FeatureName' => 'Feature name',
'DataFeatureRemoval:Table:Analysis:Occurence' => 'Occurence',
'DataFeatureRemoval:Table:Analysis:Occurrence' => 'Occurrence',
'UI:Button:Analyze' => 'Analyze',
'UI:Button:ModifyChoices' => 'Modify Choices',
'UI:Button:AnalyzeAndSetup' => 'Analyze and go to setup',
'UI:Button:PlanDeletion' => 'Prepare deletion plan',
'UI:Button:DoDeletion' => 'Delete data',
'UI:Button:BackToMain' => 'Back to Feature Removal',
'UI:Action:ForceUninstall' => 'Force uninstall',
'UI:Action:MoreInfo' => 'More information',
'DataFeatureRemoval:Table:Analysis:RemovalType:FINAL_CLASS' => 'Final class',
'DataFeatureRemoval:Table:Empty' => 'No data to remove',
]);

View File

@@ -29,7 +29,7 @@ class DataFeatureRemovalController extends Controller
{
private array $aSelectedExtensionsForCheck = [];
public function OperationMain($sErrorMessage = null)
public function OperationMain($sErrorMessage = null): void
{
$aParams = [];
@@ -40,16 +40,17 @@ class DataFeatureRemovalController extends Controller
$this->AddFeatureParams($aParams);
$this->AddAnalyzeParams($aParams);
$aParams['DataFeatureRemovalErrorMessage'] = $sErrorMessage;
$aParams['bHasData'] = count($aParams['aClasses']) > 0;
$this->DisplayPage($aParams);
}
public function AddFeatureParams(array &$aParams)
public function AddFeatureParams(array &$aParams): void
{
$aParams['aExtensions'] = $this->GetExtensionsTable();
$aParams['sModule'] = DataFeatureRemovalHelper::MODULE_NAME;
}
public function AddAnalyzeParams(array &$aParams)
public function AddAnalyzeParams(array &$aParams): void
{
$iTotalCount = 0;
$aData = [];
@@ -65,7 +66,7 @@ class DataFeatureRemovalController extends Controller
$sTypeDesc = \Dict::S("DataFeatureRemoval:Table:Analysis:RemovalType:$sTypeName");
$iCount = $oRule->Get('count');
$iTotalCount += $iCount;
$aColumns = ['ClassName', 'RemovalType','FeatureName','Occurence'];
$aColumns = ['ClassName', 'RemovalType','FeatureName','Occurrence'];
$aData[] = [
<<<HTML
<label>$sContent</label>
@@ -87,7 +88,7 @@ HTML,
$aParams['aClasses'] = $aClasses;
}
public function OperationAnalyze()
public function OperationAnalyze(): void
{
$this->ValidateTransactionId();
$aSelectedExtensionsFromUI = utils::ReadPostedParam('aExtensions', []);
@@ -174,7 +175,7 @@ HTML,
];
}
private function Analyze()
private function Analyze(): void
{
DataFeatureRemoverExtensionService::GetInstance()->SaveExtensions($this->aSelectedExtensionsForCheck);
@@ -187,14 +188,14 @@ HTML,
$this->Save($oSetupAudit->GetIssues());
}
private function Save(array $aGetRemovedClasses)
private function Save(array $aGetRemovedClasses): void
{
IssueLog::Debug(__METHOD__, null, ['aGetRemovedClasses' => $aGetRemovedClasses]);
DataFeatureRemoverAuditRuleService::GetInstance()->SaveChecks($aGetRemovedClasses);
}
public function OperationDeletionPlan()
public function OperationDeletionPlan(): void
{
$aParams = [];
$this->ValidateTransactionId();
@@ -220,7 +221,7 @@ HTML,
$this->DisplayPage($aParams);
}
public function OperationDoDeletion()
public function OperationDoDeletion(): void
{
$aParams = [];
$this->ValidateTransactionId();
@@ -245,7 +246,7 @@ HTML,
/**
* @return void
* @throws \Combodo\iTop\MFABase\Helper\MFABaseException
* @throws \Combodo\iTop\DataFeatureRemoval\Helper\DataFeatureRemovalException
*/
public function ValidateTransactionId(): void
{

View File

@@ -30,7 +30,20 @@ class DataFeatureRemoverAuditRuleService
self::$oInstance = $oInstance;
}
public function SaveChecks(array $aGetRemovedClasses)
/**
* @param array $aGetRemovedClasses
*
* @return void
* @throws \ArchivedObjectException
* @throws \CoreCannotSaveObjectException
* @throws \CoreException
* @throws \CoreUnexpectedValue
* @throws \DeleteException
* @throws \MySQLException
* @throws \MySQLHasGoneAwayException
* @throws \OQLException
*/
public function SaveChecks(array $aGetRemovedClasses): void
{
$oSearch = DBObjectSearch::FromOQL('SELECT DataFeatureRemoverAuditRule', []);
$oSearch->AllowAllData();
@@ -50,6 +63,10 @@ class DataFeatureRemoverAuditRuleService
}
}
/**
* @return array
* @throws \Combodo\iTop\DataFeatureRemoval\Helper\DataFeatureRemovalException
*/
public function ReadCheckRules(): array
{
try {

View File

@@ -33,7 +33,20 @@ class DataFeatureRemoverExtensionService
self::$oInstance = $oInstance;
}
public function SaveExtensions(array $aSelectedExtensionsForCheck)
/**
* @param array $aSelectedExtensionsForCheck
*
* @return void
* @throws \ArchivedObjectException
* @throws \CoreCannotSaveObjectException
* @throws \CoreException
* @throws \CoreUnexpectedValue
* @throws \DeleteException
* @throws \MySQLException
* @throws \MySQLHasGoneAwayException
* @throws \OQLException
*/
public function SaveExtensions(array $aSelectedExtensionsForCheck): void
{
$this->ReadItopExtensions();
@@ -60,6 +73,11 @@ class DataFeatureRemoverExtensionService
private array $aSelectedExtensions = [];
private array $aItopExtensions = [];
private array $aIncludingExtensionsByModuleName = [];
/**
* @return array
* @throws \Combodo\iTop\DataFeatureRemoval\Helper\DataFeatureRemovalException
*/
public function ReadAuditedExtensions(): array
{
if (count($this->aSelectedExtensions) == 0) {
@@ -96,6 +114,12 @@ class DataFeatureRemoverExtensionService
return $this->aSelectedExtensions;
}
/**
* @param string $sModuleName
*
* @return array
* @throws \Combodo\iTop\DataFeatureRemoval\Helper\DataFeatureRemovalException
*/
public function GetIncludingExtensions(string $sModuleName): array
{
$this->ReadAuditedExtensions();

View File

@@ -5,7 +5,6 @@ namespace Combodo\iTop\DataFeatureRemoval\Service;
use Combodo\iTop\DataFeatureRemoval\Entity\DeletionPlanSummaryEntity;
use Combodo\iTop\DataFeatureRemoval\Helper\DataFeatureRemovalException;
use DeletionPlan;
use Hoa\Math\Test\Unit\Issue;
class DeletionPlanService
{
@@ -30,13 +29,17 @@ class DeletionPlanService
}
/**
* @param array $sClasses
* @param array $aClasses
*
* @return array<\Combodo\iTop\DataFeatureRemoval\Entity\DeletionPlanSummaryEntity>
* @throws \CoreException
*/
public function GetDeletionPlanSummary(array $aClasses): array
public function GetDeletionPlanSummary(?array $aClasses): array
{
$aSummary = [];
if (is_null($aClasses)) {
return $aSummary;
}
$oDeletionPlan = new DeletionPlan();
foreach ($aClasses as $sClass) {
@@ -67,7 +70,13 @@ class DeletionPlanService
}
/**
* @param string $sClass
*
* @return \DBObject[]
* @throws \CoreException
* @throws \CoreUnexpectedValue
* @throws \MySQLException
* @throws \Exception
*/
private function GetAllObjects(string $sClass): array
{
@@ -78,11 +87,15 @@ class DeletionPlanService
}
/**
* @param array $sClasses
* @param array $aClasses
*
* @return array<\Combodo\iTop\DataFeatureRemoval\Entity\DeletionPlanSummaryEntity>
* @throws \Combodo\iTop\DataFeatureRemoval\Helper\DataFeatureRemovalException
* @throws \CoreException
* @throws \CoreUnexpectedValue
* @throws \MySQLException
*/
public function ExecuteDeletionPlan(array $aClasses)
public function ExecuteDeletionPlan(array $aClasses): array
{
$oDeletionPlan = new DeletionPlan();
foreach ($aClasses as $sClass) {
@@ -100,8 +113,15 @@ class DeletionPlanService
* @param DeletionPlan $oDeletionPlan
*
* @return array<\Combodo\iTop\DataFeatureRemoval\Entity\DeletionPlanSummaryEntity>
* @throws \Combodo\iTop\DataFeatureRemoval\Helper\DataFeatureRemovalException
* @throws \CoreCannotSaveObjectException
* @throws \CoreException
* @throws \CoreUnexpectedValue
* @throws \MySQLException
* @throws \MySQLHasGoneAwayException
* @throws \OQLException
*/
private function DoDelete(DeletionPlan $oDeletionPlan)
private function DoDelete(DeletionPlan $oDeletionPlan): array
{
if (count($oDeletionPlan->GetIssues()) > 0) {
throw new DataFeatureRemovalException("Deletion Plan cannot be executed due to issues");
@@ -129,10 +149,12 @@ class DeletionPlanService
$oDeletionPlanSummaryEntity = $aSummary[$sClass] ?? new DeletionPlanSummaryEntity($sClass);
foreach ($aDeletes as $sId => $aDelete) {
$oFilter = \DBObjectSearch::FromOQL_AllData("SELECT $sClass WHERE id=:id");
$sQuery = $oFilter->MakeDeleteQuery(['id' => $sId]);
\CMDBSource::DeleteFrom($sQuery);
\IssueLog::Info(__METHOD__, null, [$sQuery]);
foreach (\MetaModel::EnumParentClasses($sClass, ENUM_PARENT_CLASSES_ALL, false) as $sParentClass) {
$oFilter = \DBObjectSearch::FromOQL_AllData("SELECT $sParentClass WHERE id=:id");
$sQuery = $oFilter->MakeDeleteQuery(['id' => $sId]);
\CMDBSource::DeleteFrom($sQuery);
\IssueLog::Info($sQuery);
}
$oDeletionPlanSummaryEntity->iDeleteCount++;
}

View File

@@ -5,14 +5,13 @@
{% UIDataTable ForForm { sRef:'aDeletionPlanSummary', aColumns:aDeletionPlanSummary.Columns, aData:aDeletionPlanSummary.Data} %}{% EndUIDataTable %}
{% EndUIPanel %}
{% UIForm Standard {} %}
{% UIInput ForHidden { sName:'transaction_id', sValue:sTransactionId} %}
{% UIInput ForHidden { sName:'operation', sValue:'DoDeletion'} %}
{% for sKey, sClass in aClasses %}
{% UIInput ForHidden { sName:"classes[" ~ sKey ~ "]", sValue:sClass } %}
{% endfor %}
{% UIToolbar ForButton {} %}
{% UIToolbar ForButton {} %}
{% UIButton ForPrimaryAction {sLabel:'UI:Button:DoDeletion'|dict_s, sName:'btn_deletion', sId:'btn_deletion', bIsSubmit:true} %}
{% EndUIToolbar %}
{% EndUIForm %}
@@ -20,7 +19,7 @@
{% UIForm Standard {} %}
{% UIInput ForHidden { sName:'transaction_id', sValue:sTransactionId} %}
{% UIInput ForHidden { sName:'operation', sValue:'Main'} %}
{% UIToolbar ForButton {} %}
{% UIToolbar ForButton {} %}
{% UIButton ForPrimaryAction {sLabel:'UI:Button:BackToMain'|dict_s, sName:'btn_deletion', sId:'btn_deletion', bIsSubmit:true} %}
{% EndUIToolbar %}
{% EndUIForm %}

View File

@@ -8,7 +8,7 @@
{% UIForm Standard {} %}
{% UIInput ForHidden { sName:'transaction_id', sValue:sTransactionId} %}
{% UIInput ForHidden { sName:'operation', sValue:'Main'} %}
{% UIToolbar ForButton {} %}
{% UIToolbar ForButton {} %}
{% UIButton ForPrimaryAction {sLabel:'UI:Button:BackToMain'|dict_s, sName:'btn_deletion', sId:'btn_deletion', bIsSubmit:true} %}
{% EndUIToolbar %}
{% EndUIForm %}

View File

@@ -1,17 +1,19 @@
{# @copyright Copyright (C) 2010-2024 Combodo SAS #}
{# @license http://opensource.org/licenses/AGPL-3.0 #}
{% UIPanel Neutral { sTitle:'DataFeatureRemoval:Analysis:Title'|dict_s, sSubTitle: 'DataFeatureRemoval:Analysis:SubTitle'|dict_format(rule_count) } %}
{% UIDataTable ForForm { sRef:'aCheckRules', aColumns:aCheckRules.Columns, aData:aCheckRules.Data} %}{% EndUIDataTable %}
{% EndUIPanel %}
{% if bHasData %}
{% UIPanel Neutral { sTitle:'DataFeatureRemoval:Analysis:Title'|dict_s, sSubTitle: 'DataFeatureRemoval:Analysis:SubTitle'|dict_format(rule_count) } %}
{% UIDataTable ForForm { sRef:'aCheckRules', aColumns:aCheckRules.Columns, aData:aCheckRules.Data} %}{% EndUIDataTable %}
{% EndUIPanel %}
{% UIForm Standard {} %}
{% UIInput ForHidden { sName:'transaction_id', sValue:sTransactionId} %}
{% UIInput ForHidden { sName:'operation', sValue:'DeletionPlan'} %}
{% for sKey, sClass in aClasses %}
{% UIInput ForHidden { sName:"classes[" ~ sKey ~ "]", sValue:sClass } %}
{% endfor %}
{% UIToolbar ForButton {} %}
{% UIButton ForPrimaryAction {sLabel:'UI:Button:PlanDeletion'|dict_s, sName:'btn_plandeletion', sId:'btn_plandeletion', bIsSubmit:true} %}
{% EndUIToolbar %}
{% EndUIForm %}
{% UIForm Standard {} %}
{% UIInput ForHidden { sName:'transaction_id', sValue:sTransactionId} %}
{% UIInput ForHidden { sName:'operation', sValue:'DeletionPlan'} %}
{% for sKey, sClass in aClasses %}
{% UIInput ForHidden { sName:"classes[" ~ sKey ~ "]", sValue:sClass } %}
{% endfor %}
{% UIToolbar ForButton {} %}
{% UIButton ForPrimaryAction {sLabel:'UI:Button:PlanDeletion'|dict_s, sName:'btn_plandeletion', sId:'btn_plandeletion', bIsSubmit:true} %}
{% EndUIToolbar %}
{% EndUIForm %}
{% endif %}

View File

@@ -10,9 +10,7 @@
{% UIDataTable ForForm { sRef:'aExtensions', aColumns:aExtensions.Columns, aData:aExtensions.Data} %}{% EndUIDataTable %}
{% EndUIFieldSet %}
{% UIToolbar ForButton {} %}
{% UIButton ForPrimaryAction {sLabel:'UI:Button:Analyze'|dict_s, sName:'btn_apply', sId:'btn_apply', bIsSubmit:true} %}
{% EndUIToolbar %}
{% EndUIForm %}

View File

@@ -15,13 +15,12 @@
{{ 'DataFeatureRemoval:Helper:Desc2'|dict_s }}
{% EndUIAlert %}
{% if null != DataFeatureRemovalErrorMessage %}
<div id="feature_removal_error_msg_div" style="display:block">
{% UIAlert ForFailure { sTitle:'DataFeatureRemoval:Failure:Title'|dict_s, sId: 'feature_removal_error_msg', sContent:DataFeatureRemovalErrorMessage } %}
{% EndUIAlert %}
</div>
{% endif %}
{% if null != DataFeatureRemovalErrorMessage %}
<div id="feature_removal_error_msg_div" style="display:block">
{% UIAlert ForFailure { sTitle:'DataFeatureRemoval:Failure:Title'|dict_s, sId: 'feature_removal_error_msg', sContent:DataFeatureRemovalErrorMessage } %}
{% EndUIAlert %}
</div>
{% endif %}
{% include 'Features.html.twig' %}
{% include 'ExtensionRemovalData.html.twig' %}

View File

@@ -2,7 +2,7 @@
{# @license http://opensource.org/licenses/AGPL-3.0 #}
$(document).on('click', '#checkAllExtensions', function() {
var bChecked = this.checked;
let bChecked = this.checked;
$('.extension_check').each( function() { this.checked = bChecked });
});