mirror of
https://github.com/Combodo/iTop.git
synced 2026-07-26 00:26:39 +02:00
Compare commits
9 Commits
3.3.0-beta
...
faf/faf-da
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ae4ed11729 | ||
|
|
42ade7e1be | ||
|
|
4ce0cd7173 | ||
|
|
eeb1312127 | ||
|
|
13fad9efc1 | ||
|
|
9fbc53cf00 | ||
|
|
918f110dc9 | ||
|
|
bd9cc9c15e | ||
|
|
60ce21509f |
@@ -5368,6 +5368,7 @@ JS
|
||||
/**
|
||||
* @param array $aChanges
|
||||
* @param bool $bIsNew
|
||||
* @param string|null $sStimulusBeingApplied
|
||||
*
|
||||
* @return void
|
||||
* @throws \ArchivedObjectException
|
||||
@@ -5381,6 +5382,20 @@ JS
|
||||
$this->FireEvent(EVENT_DB_AFTER_WRITE, ['is_new' => $bIsNew, 'changes' => $aChanges, 'stimulus_applied' => $sStimulusBeingApplied, 'cmdb_change' => self::GetCurrentChange()]);
|
||||
}
|
||||
|
||||
//////////////
|
||||
/// READ
|
||||
///
|
||||
|
||||
/**
|
||||
* @return void
|
||||
* @throws \CoreException
|
||||
* @since 3.3.0
|
||||
*/
|
||||
final public function FireEventReadDetails(string $sExportType): void
|
||||
{
|
||||
$this->FireEvent(EVENT_DATA_EXPORT, ['export_type' => $sExportType]);
|
||||
}
|
||||
|
||||
//////////////
|
||||
/// DELETE
|
||||
///
|
||||
|
||||
@@ -519,6 +519,31 @@ Call $this->AddInitialAttributeFlags($sAttCode, $iFlags) for all the initial att
|
||||
</event_datum>
|
||||
</event_data>
|
||||
</event>
|
||||
<event id="EVENT_DATA_EXPORT" _delta="define">
|
||||
<name>Object details read from outside iTop</name>
|
||||
<description><![CDATA[An object details has been read during an export]]></description>
|
||||
<sources>
|
||||
<source id="cmdbAbstractObject">cmdbAbstractObject</source>
|
||||
</sources>
|
||||
<event_data>
|
||||
<event_datum id="object">
|
||||
<description>The object unarchived</description>
|
||||
<type>DBObject</type>
|
||||
</event_datum>
|
||||
<event_datum id="attributes">
|
||||
<description>Attribute codes exposed (empty means potentially all attributes)</description>
|
||||
<type>array</type>
|
||||
</event_datum>
|
||||
<event_datum id="export_type">
|
||||
<description>Type of export</description>
|
||||
<type>string</type>
|
||||
</event_datum>
|
||||
<event_datum id="debug_info">
|
||||
<description>Debug string</description>
|
||||
<type>string</type>
|
||||
</event_datum>
|
||||
</event_data>
|
||||
</event>
|
||||
<event id="EVENT_DOWNLOAD_DOCUMENT" _delta="define">
|
||||
<name>Document downloaded</name>
|
||||
<description><![CDATA[A document has been downloaded from the GUI]]></description>
|
||||
|
||||
@@ -25,11 +25,11 @@
|
||||
*/
|
||||
|
||||
use Combodo\iTop\Application\Branding;
|
||||
use Combodo\iTop\Application\Helper\Session;
|
||||
use Combodo\iTop\Application\WebPage\ErrorPage;
|
||||
use Combodo\iTop\Application\WebPage\NiceWebPage;
|
||||
use Combodo\iTop\Service\Events\EventData;
|
||||
use Combodo\iTop\Service\Events\EventService;
|
||||
use Combodo\iTop\Service\Session\Session;
|
||||
|
||||
/**
|
||||
* Web page used for displaying the login form
|
||||
@@ -369,16 +369,7 @@ class LoginWebPage extends NiceWebPage
|
||||
public static function ResetSession()
|
||||
{
|
||||
// Unset all of the session variables.
|
||||
Session::Unset('auth_user');
|
||||
Session::Unset('login_state');
|
||||
Session::Unset('can_logoff');
|
||||
Session::Unset('archive_mode');
|
||||
Session::Unset('impersonate_user');
|
||||
Session::Unset('PluginProperties');
|
||||
Session::Unset('UrlMakerClass');
|
||||
Session::Unset('itop_env');
|
||||
Session::Unset('obj_messages');
|
||||
Session::Unset('profile_list');
|
||||
Session::UnsetAll();
|
||||
UserRights::_ResetSessionCache();
|
||||
// If it's desired to kill the session, also delete the session cookie.
|
||||
// Note: This will destroy the session, and not just the session data!
|
||||
|
||||
@@ -334,12 +334,14 @@ EOF
|
||||
while ($aRow = $oSet->FetchAssoc()) {
|
||||
set_time_limit(intval($iLoopTimeLimit));
|
||||
$aData = [];
|
||||
foreach ($this->aStatusInfo['fields'] as $iCol => $aFieldSpec) {
|
||||
$aExportedObjects = [];
|
||||
foreach ($this->aStatusInfo['fields'] as $aFieldSpec) {
|
||||
$sAlias = $aFieldSpec['sAlias'];
|
||||
$sAttCode = $aFieldSpec['sAttCode'];
|
||||
|
||||
$sField = '';
|
||||
$oObj = $aRow[$sAlias];
|
||||
$aExportedObjects[] = $oObj;
|
||||
if ($oObj != null) {
|
||||
switch ($sAttCode) {
|
||||
case 'id':
|
||||
@@ -366,7 +368,13 @@ EOF
|
||||
}
|
||||
$sData .= implode($this->aStatusInfo['separator'], $aData)."\n";
|
||||
$iCount++;
|
||||
|
||||
$aExportedObjects = array_unique($aExportedObjects);
|
||||
foreach ($aExportedObjects as $oExportedObject) {
|
||||
$oExportedObject->FireEventReadDetails(get_class($this));
|
||||
}
|
||||
}
|
||||
|
||||
// Restore original date & time formats
|
||||
AttributeDateTime::SetFormat($oPrevDateTimeFormat);
|
||||
AttributeDate::SetFormat($oPrevDateFormat);
|
||||
|
||||
@@ -6247,7 +6247,9 @@ abstract class DBObject implements iDisplay
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $aChanges
|
||||
* @param bool $bIsNew
|
||||
* @param string|null $sStimulusBeingApplied
|
||||
*
|
||||
* @return void
|
||||
* @since 3.1.0
|
||||
@@ -6256,6 +6258,18 @@ abstract class DBObject implements iDisplay
|
||||
{
|
||||
}
|
||||
|
||||
//////////////
|
||||
/// READ
|
||||
///
|
||||
|
||||
/**
|
||||
* @return void
|
||||
* @since 3.3.0
|
||||
*/
|
||||
public function FireEventReadDetails(string $sExportType): void
|
||||
{
|
||||
}
|
||||
|
||||
//////////////
|
||||
/// DELETE
|
||||
///
|
||||
|
||||
@@ -288,11 +288,13 @@ EOF
|
||||
while ($aRow = $oSet->FetchAssoc()) {
|
||||
set_time_limit(intval($iLoopTimeLimit));
|
||||
$aData = [];
|
||||
$aExportedObjects = [];
|
||||
foreach ($this->aStatusInfo['fields'] as $iCol => $aFieldSpec) {
|
||||
$sAlias = $aFieldSpec['sAlias'];
|
||||
$sAttCode = $aFieldSpec['sAttCode'];
|
||||
|
||||
$oObj = $aRow[$sAlias];
|
||||
$aExportedObjects[] = $oObj;
|
||||
$sField = '';
|
||||
if ($oObj) {
|
||||
$sField = $this->GetValue($oObj, $sAttCode);
|
||||
@@ -301,6 +303,11 @@ EOF
|
||||
}
|
||||
fwrite($hFile, json_encode($aData)."\n");
|
||||
$iCount++;
|
||||
|
||||
$aExportedObjects = array_unique($aExportedObjects);
|
||||
foreach ($aExportedObjects as $oExportedObject) {
|
||||
$oExportedObject->FireEventReadDetails(get_class($this));
|
||||
}
|
||||
}
|
||||
set_time_limit(intval($iPreviousTimeLimit));
|
||||
$this->aStatusInfo['position'] += $this->iChunkSize;
|
||||
|
||||
@@ -139,6 +139,7 @@ class HTMLBulkExport extends TabularBulkExport
|
||||
} else {
|
||||
$sData .= "<tr>";
|
||||
}
|
||||
$aExportedObjects = [];
|
||||
foreach ($this->aStatusInfo['fields'] as $iCol => $aFieldSpec) {
|
||||
$sAlias = $aFieldSpec['sAlias'];
|
||||
$sAttCode = $aFieldSpec['sAttCode'];
|
||||
@@ -147,12 +148,18 @@ class HTMLBulkExport extends TabularBulkExport
|
||||
$sField = '';
|
||||
if ($oObj) {
|
||||
$sField = $this->GetValue($oObj, $sAttCode);
|
||||
$aExportedObjects[] = $oObj;
|
||||
}
|
||||
$sValue = ($sField === '') ? ' ' : $sField;
|
||||
$sData .= "<td>$sValue</td>";
|
||||
}
|
||||
$sData .= "</tr>";
|
||||
$iCount++;
|
||||
|
||||
$aExportedObjects = array_unique($aExportedObjects);
|
||||
foreach ($aExportedObjects as $oExportedObject) {
|
||||
$oExportedObject->FireEventReadDetails(get_class($this));
|
||||
}
|
||||
}
|
||||
set_time_limit(intval($iPreviousTimeLimit));
|
||||
$this->aStatusInfo['position'] += $this->iChunkSize;
|
||||
|
||||
@@ -625,6 +625,7 @@ class CoreServices implements iRestServiceProvider, iRestInputSanitizer
|
||||
}
|
||||
|
||||
while ($oObject = $oObjectSet->Fetch()) {
|
||||
$oObject->FireEventReadDetails(get_class($this));
|
||||
$oResult->AddObject(0, '', $oObject, $aShowFields, RestUtils::HasRequestedExtendedOutput($sShowFields));
|
||||
}
|
||||
$oResult->message = "Found: ".$oObjectSet->Count();
|
||||
@@ -699,6 +700,7 @@ class CoreServices implements iRestServiceProvider, iRestInputSanitizer
|
||||
if ($oElement instanceof RelationObjectNode) {
|
||||
$oObject = $oElement->GetProperty('object');
|
||||
if ($oObject) {
|
||||
$oObject->FireEventReadDetails(get_class($this));
|
||||
if ($bEnableRedundancy && $sDirection == 'down') {
|
||||
// Add only the "reached" objects
|
||||
if ($oElement->GetProperty('is_reached')) {
|
||||
|
||||
@@ -233,7 +233,6 @@ EOF
|
||||
public function GetNextChunk(&$aStatus)
|
||||
{
|
||||
$sRetCode = 'run';
|
||||
$iPercentage = 0;
|
||||
|
||||
$oSet = new DBObjectSet($this->oSearch);
|
||||
$oSet->SetLimit($this->iChunkSize, $this->aStatusInfo['position']);
|
||||
@@ -254,7 +253,8 @@ EOF
|
||||
set_time_limit(intval($iLoopTimeLimit));
|
||||
|
||||
$sData .= "<tr>";
|
||||
foreach ($this->aStatusInfo['fields'] as $iCol => $aFieldSpec) {
|
||||
$aExportedObjects = [];
|
||||
foreach ($this->aStatusInfo['fields'] as $aFieldSpec) {
|
||||
$sAlias = $aFieldSpec['sAlias'];
|
||||
$sAttCode = $aFieldSpec['sAttCode'];
|
||||
|
||||
@@ -265,7 +265,7 @@ EOF
|
||||
$sData .= "<td x:str></td>";
|
||||
continue;
|
||||
}
|
||||
|
||||
$aExportedObjects[] = $oObj;
|
||||
switch ($sAttCode) {
|
||||
case 'id':
|
||||
$sField = $oObj->GetKey();
|
||||
@@ -322,6 +322,11 @@ EOF
|
||||
}
|
||||
$sData .= "</tr>";
|
||||
$iCount++;
|
||||
|
||||
$aExportedObjects = array_unique($aExportedObjects);
|
||||
foreach ($aExportedObjects as $oExportedObject) {
|
||||
$oExportedObject->FireEventReadDetails(get_class($this));
|
||||
}
|
||||
}
|
||||
set_time_limit(intval($iPreviousTimeLimit));
|
||||
$this->aStatusInfo['position'] += $this->iChunkSize;
|
||||
|
||||
@@ -135,6 +135,11 @@ abstract class Trigger extends cmdbAbstractObject
|
||||
if ($oAction->IsActive()) {
|
||||
$oKPI = new ExecutionKPI();
|
||||
$aContextArgs['action->object()'] = $oAction;
|
||||
if (array_key_exists('this->object()', $aContextArgs)) {
|
||||
/** @var \DBObject $oObject */
|
||||
$oObject = $aContextArgs['this->object()'];
|
||||
$oObject->FireEventReadDetails(get_class($oAction));
|
||||
}
|
||||
$oAction->DoExecute($this, $aContextArgs);
|
||||
$oKPI->ComputeStatsForExtension($oAction, 'DoExecute');
|
||||
}
|
||||
|
||||
@@ -144,6 +144,7 @@ class XMLBulkExport extends BulkExport
|
||||
if (count($aAuthorizedClasses) > 1) {
|
||||
$sData .= "<Row>\n";
|
||||
}
|
||||
$aExportedObjects = [];
|
||||
foreach ($aAuthorizedClasses as $sAlias => $sClassName) {
|
||||
$oObj = $aObjects[$sAlias];
|
||||
if (is_null($oObj)) {
|
||||
@@ -151,6 +152,7 @@ class XMLBulkExport extends BulkExport
|
||||
} else {
|
||||
$sClassName = get_class($oObj);
|
||||
$sData .= "<$sClassName alias=\"$sAlias\" id=\"".$oObj->GetKey()."\">\n";
|
||||
$aExportedObjects[] = $oObj;
|
||||
}
|
||||
foreach ($aClass2Attributes[$sAlias] as $sAttCode => $oAttDef) {
|
||||
if (is_null($oObj)) {
|
||||
@@ -166,6 +168,11 @@ class XMLBulkExport extends BulkExport
|
||||
$sData .= "</Row>\n";
|
||||
}
|
||||
$iCount++;
|
||||
|
||||
$aExportedObjects = array_unique($aExportedObjects);
|
||||
foreach ($aExportedObjects as $oExportedObject) {
|
||||
$oExportedObject->FireEventReadDetails(get_class($this));
|
||||
}
|
||||
}
|
||||
|
||||
set_time_limit(intval($iPreviousTimeLimit));
|
||||
|
||||
@@ -347,7 +347,7 @@ class DataFeatureRemovalController extends Controller
|
||||
'uninstallable' => $oExtension->CanBeUninstalled(),
|
||||
'remote' => $oExtension->IsRemote(),
|
||||
'missing' => $oExtension->bRemovedFromDisk,
|
||||
'dependency_issue' => $oExtension->HasDependencyIssue(),
|
||||
'cannot-be-installed' => $oExtension->HasDependencyIssue(),
|
||||
],
|
||||
|
||||
];
|
||||
@@ -442,7 +442,7 @@ class DataFeatureRemovalController extends Controller
|
||||
if (! $this->bForcedUninstallation && $aExtensionData['extra_flags']['uninstallable']) {
|
||||
$this->bForcedUninstallation = true;
|
||||
}
|
||||
if (false === $aExtensionData['extra_flags']['uninstallable'] || true === $aExtensionData['extra_flags']['remote']) {
|
||||
if (false === $aExtensionData['extra_flags']['uninstallable']) {
|
||||
$this->aExtensionsToCheck['extensions_not_uninstallable'][] = $sCode;
|
||||
}
|
||||
} elseif (!$aExtensionData['installed'] && $aSelectedExtensionsFromUI[$sCode] === 'on') {
|
||||
|
||||
@@ -134,6 +134,10 @@ class DataCleanupService
|
||||
/** @var DBObject $oDependentObj */
|
||||
while ($oDependentObj = $oSet->Fetch()) {
|
||||
$iDeletePropagationOption = $oExtKeyAttDef->GetDeletionPropagationOption();
|
||||
if ($iDeletePropagationOption == DEL_MANUAL) {
|
||||
$this->oObjectService->SetIssue(get_class($oDependentObj));
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($oExtKeyAttDef->IsNullAllowed()) {
|
||||
// Optional external key, list to reset
|
||||
@@ -148,12 +152,6 @@ class DataCleanupService
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
// Mandatory external key
|
||||
if ($iDeletePropagationOption == DEL_MANUAL) {
|
||||
// Cannot be deleted automatically, must be handled manually
|
||||
$this->oObjectService->SetIssue(get_class($oDependentObj));
|
||||
continue;
|
||||
}
|
||||
// Propagate deletion only if not visited
|
||||
if ($this->IsVisited($oDependentObj)) {
|
||||
continue;
|
||||
|
||||
@@ -59,11 +59,9 @@ class StaticDeletionPlan
|
||||
{
|
||||
foreach ($aClasses as $sClass) {
|
||||
$oDeletionPlanItem = $this->GetInitialClassDeletionPlan($sClass);
|
||||
// N°9831 Do not overwrite existing entity as it may already exist for this class if a previously processed class references it (issues/updates already accumulated must be kept)
|
||||
if (false === array_key_exists($sClass, $this->aDeletionPlan)) {
|
||||
$this->aDeletionPlan[$sClass] = new DeletionPlanEntity();
|
||||
}
|
||||
$this->aDeletionPlan[$sClass]->oDelete->Merge($oDeletionPlanItem);
|
||||
$oDeletionPlanEntity = new DeletionPlanEntity();
|
||||
$oDeletionPlanEntity->oDelete->Merge($oDeletionPlanItem);
|
||||
$this->aDeletionPlan[$sClass] = $oDeletionPlanEntity;
|
||||
|
||||
$this->DeletionPlanForReferencingClasses($sClass);
|
||||
}
|
||||
|
||||
@@ -49,8 +49,6 @@ if (Session::IsSet('auth_user')) {
|
||||
UserRights::Login($sAuthUser); // Set the user's language
|
||||
}
|
||||
|
||||
LoginWebPage::ResetSession();
|
||||
|
||||
$bLoginDebug = MetaModel::GetConfig()->Get('login_debug');
|
||||
if ($bLoginDebug) {
|
||||
IssueLog::Info("---------------------------------");
|
||||
@@ -88,7 +86,7 @@ if ($bLoginDebug) {
|
||||
IssueLog::Info("--> Display logout page");
|
||||
}
|
||||
|
||||
LoginWebPage::ResetSession(true);
|
||||
LoginWebPage::ResetSession();
|
||||
if ($bLoginDebug) {
|
||||
$sSessionLog = session_id().' '.utils::GetSessionLog();
|
||||
IssueLog::Info("SESSION: $sSessionLog");
|
||||
|
||||
@@ -8,7 +8,6 @@ use DBObjectSet;
|
||||
use IssueLog;
|
||||
use MetaModel;
|
||||
use SetupLog;
|
||||
use TagSetFieldData;
|
||||
|
||||
require_once APPROOT.'setup/feature_removal/ModelReflectionSerializer.php';
|
||||
|
||||
@@ -69,10 +68,6 @@ abstract class AbstractSetupAudit
|
||||
continue;
|
||||
}
|
||||
|
||||
if (MetaModel::IsSameFamily($sClass, TagSetFieldData::class)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!MetaModel::IsStandaloneClass($sClass)) {
|
||||
$iCount = $this->Count($sClass);
|
||||
$this->aFinalClassesToCleanup[$sClass] = $iCount;
|
||||
|
||||
@@ -176,7 +176,7 @@ class iTopExtension
|
||||
};
|
||||
}
|
||||
|
||||
public function IsRemote(): bool
|
||||
public function IsRemote(): string
|
||||
{
|
||||
return $this->sSource === self::SOURCE_REMOTE;
|
||||
}
|
||||
|
||||
@@ -933,7 +933,6 @@ class RunTimeEnvironment
|
||||
@chmod($sFinalConfig, 0440); // Read-only for owner and group, nothing for others
|
||||
|
||||
SetupUtils::rrmdir(dirname($sBuildConfig)); // Cleanup the temporary build dir if empty
|
||||
MetaModel::ResetAllCaches($this->sBuildEnv);
|
||||
MetaModel::ResetAllCaches($this->sFinalEnv);
|
||||
|
||||
if (! isset($_SESSION)) {
|
||||
|
||||
@@ -750,8 +750,8 @@ EOF
|
||||
$bSelected = isset($aSelectedComponents[$sChoiceId]) && ($aSelectedComponents[$sChoiceId] === $sChoiceId);
|
||||
$bMissingFromDisk = isset($aChoice['missing']) && $aChoice['missing'] === true;
|
||||
$bMandatory = (isset($aChoice['mandatory']) && $aChoice['mandatory']);
|
||||
$bInstalled = $bMissingFromDisk || $oITopExtension?->bInstalled ?? false;
|
||||
$bDependencyIssue = $oITopExtension?->HasDependencyIssue() ?? false;
|
||||
$bInstalled = $bMissingFromDisk || $oITopExtension->bInstalled;
|
||||
$bDependencyIssue = $oITopExtension->HasDependencyIssue();
|
||||
|
||||
$bChecked = $bSelected;
|
||||
$bDisabled = false;
|
||||
|
||||
@@ -131,6 +131,23 @@ class Session
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unset all session variables, no matter if they were set by iTop or not
|
||||
*
|
||||
* @return void
|
||||
* @since 3.3.0 N°9625
|
||||
*/
|
||||
public static function UnsetAll(): void
|
||||
{
|
||||
if (session_status() !== PHP_SESSION_ACTIVE) {
|
||||
self::Start();
|
||||
$_SESSION = [];
|
||||
self::WriteClose();
|
||||
} else {
|
||||
$_SESSION = [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|array $key key to access to the session variable. To access to $_SESSION['a']['b'] $key must be ['a', 'b']
|
||||
* @param $default
|
||||
@@ -183,6 +200,10 @@ class Session
|
||||
|
||||
public static function ListVariables(): array
|
||||
{
|
||||
if (!isset($_SESSION)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return array_keys($_SESSION);
|
||||
}
|
||||
|
||||
|
||||
@@ -123,6 +123,23 @@ class SessionTest extends ItopTestCase
|
||||
$this->assertFalse(Session::IsSet(['test1', 'test2', 'test3']));
|
||||
}
|
||||
|
||||
public function testUnsetAll()
|
||||
{
|
||||
Session::Start();
|
||||
Session::Set('test', 'OK');
|
||||
Session::Set(['test1', 'test2', 'test3'], 'OK');
|
||||
Session::Set('another_test', ['foo' => 'bar']);
|
||||
$this->assertTrue(Session::IsSet('test'));
|
||||
$this->assertTrue(Session::IsSet(['test1', 'test2', 'test3']));
|
||||
$this->assertTrue(Session::IsSet('another_test'));
|
||||
|
||||
Session::UnsetAll();
|
||||
$this->assertEmpty($_SESSION);
|
||||
$this->assertFalse(Session::IsSet('test'));
|
||||
$this->assertFalse(Session::IsSet(['test1', 'test2', 'test3']));
|
||||
$this->assertFalse(Session::IsSet('another_test'));
|
||||
}
|
||||
|
||||
public function testRegenerateId()
|
||||
{
|
||||
Session::Start();
|
||||
|
||||
@@ -163,58 +163,6 @@ class DataCleanupServiceTest extends \AbstractCleanup
|
||||
$this->AssertSummaryEquals($aExpected, $aRes);
|
||||
}
|
||||
|
||||
/**
|
||||
* N°9831
|
||||
*
|
||||
* A nullable external key configured as DEL_MANUAL must NOT be treated as an issue: it is simply
|
||||
* reset (set to null), exactly like core DBObject::MakeDeletionPlan does. DEL_MANUAL only blocks
|
||||
* for mandatory keys. This mirrors the real "Hypervisor.farm_id -> Farm" case (nullable + DEL_MANUAL).
|
||||
*
|
||||
* Regression guard for the ordering of the IsNullAllowed()/DEL_MANUAL checks in RecursiveDeletion:
|
||||
* reverting that change would make ExecuteCleanup throw here instead of resetting the object.
|
||||
*/
|
||||
public function testExecuteCleanup_NullableManualShouldResetAndNotThrow()
|
||||
{
|
||||
$this->GivenDFRTreeInDB(<<<EOF
|
||||
DFRToRemoveLeaf_1 <- DFRManualNullable_1
|
||||
EOF);
|
||||
|
||||
$aClasses = [ 'DFRToRemoveLeaf' ];
|
||||
$oService = new DataCleanupService();
|
||||
// Must NOT throw: the nullable external key is reset, not reported as an issue
|
||||
$aRes = $oService->ExecuteCleanup($aClasses);
|
||||
|
||||
$aExpected = [
|
||||
['DFRManualNullable', 1, 0 ],
|
||||
['DFRToRemoveLeaf', 0, 1 ],
|
||||
];
|
||||
$this->AssertSummaryEquals($aExpected, $aRes);
|
||||
}
|
||||
|
||||
/**
|
||||
* N°9831
|
||||
*
|
||||
* Summary counterpart of the test above: a nullable DEL_MANUAL external key is reported as an
|
||||
* update (reset), with an issue count of 0. Reverting the RecursiveDeletion change would report
|
||||
* it as an issue instead.
|
||||
*/
|
||||
public function testGetCleanupSummary_NullableManualShouldBeUpdateNotIssue()
|
||||
{
|
||||
$this->GivenDFRTreeInDB(<<<EOF
|
||||
DFRToRemoveLeaf_1 <- DFRManualNullable_1
|
||||
EOF);
|
||||
|
||||
$aClasses = [ 'DFRToRemoveLeaf' ];
|
||||
$oService = new DataCleanupService();
|
||||
$aRes = $oService->GetCleanupSummary($aClasses);
|
||||
|
||||
$aExpected = [
|
||||
['DFRManualNullable', 1, 0, 0 ],
|
||||
['DFRToRemoveLeaf', 0, 1, 0 ],
|
||||
];
|
||||
$this->AssertSummaryEquals($aExpected, $aRes);
|
||||
}
|
||||
|
||||
private function AssertSummaryEquals(array $expected, $actual, $sMessage = '')
|
||||
{
|
||||
$aExpected = [];
|
||||
|
||||
@@ -9,6 +9,7 @@ namespace Combodo\iTop\Test\UnitTest\Module\DataFeatureRemoval;
|
||||
|
||||
use Combodo\iTop\DataFeatureRemoval\Entity\DataCleanupSummaryEntity;
|
||||
use Combodo\iTop\DataFeatureRemoval\Service\StaticDeletionPlan;
|
||||
use MetaModel;
|
||||
|
||||
require_once __DIR__."/AbstractCleanup.php";
|
||||
class StaticDeletionPlanTest extends \AbstractCleanup
|
||||
@@ -149,66 +150,6 @@ EOF);
|
||||
$this->AssertSummaryEquals($aExpected, $aRes);
|
||||
}
|
||||
|
||||
/**
|
||||
* N°9831
|
||||
*
|
||||
* A DEL_MANUAL issue must still be reported when the referencing class (DFRManual) is itself part
|
||||
* of the classes to remove AND is processed AFTER the class it points to (DFRToRemoveLeaf).
|
||||
*
|
||||
* This mirrors the real "VirtualMachine -> VirtualHost" case: the setup audit sorts the removed
|
||||
* classes alphabetically and drops abstract classes, so a referencing class such as VirtualMachine
|
||||
* ends up processed after its concrete targets (Farm/Hypervisor/Cloud). Here DFRToRemove is abstract
|
||||
* and DFRToRemoveLeaf is its concrete child, while DFRManual points to DFRToRemove with a mandatory
|
||||
* DEL_MANUAL external key.
|
||||
*
|
||||
* Regression: the plan entity accumulating the issue used to be overwritten when the referencing
|
||||
* class was processed as a top-level class, silently dropping the issue (no blocking message on the
|
||||
* summary page, then a crash at execution time).
|
||||
*/
|
||||
public function testGetCleanupSummary_IssueKeptWhenReferencingClassListedAfterTarget(): void
|
||||
{
|
||||
$this->GivenDFRObjectsInDB(<<<EOF
|
||||
create DFRToRemoveLeaf (name = DFRToRemoveLeaf_1)
|
||||
create DFRManual (name = DFRManual_1, extkey_id = DFRToRemoveLeaf_1)
|
||||
EOF);
|
||||
|
||||
// Target listed BEFORE the referencing class, as produced by the alphabetically sorted audit
|
||||
$aClasses = ['DFRToRemoveLeaf', 'DFRManual'];
|
||||
$oService = new StaticDeletionPlan();
|
||||
$aRes = $oService->GetCleanupSummary($aClasses);
|
||||
|
||||
$aExpected = [
|
||||
'DFRToRemoveLeaf' => ['iDeleteCount' => 1],
|
||||
'DFRManual' => ['iDeleteCount' => 1, 'iIssueCount' => 1],
|
||||
];
|
||||
$this->AssertSummaryEquals($aExpected, $aRes);
|
||||
}
|
||||
|
||||
/**
|
||||
* N°9831
|
||||
*
|
||||
* Control case: the same issue must also be reported when the referencing class is listed BEFORE
|
||||
* its target (this order already worked, e.g. "Enclosure -> Rack"). Together with the test above,
|
||||
* this locks in that issue detection is independent of the processing order.
|
||||
*/
|
||||
public function testGetCleanupSummary_IssueKeptWhenReferencingClassListedBeforeTarget(): void
|
||||
{
|
||||
$this->GivenDFRObjectsInDB(<<<EOF
|
||||
create DFRToRemoveLeaf (name = DFRToRemoveLeaf_1)
|
||||
create DFRManual (name = DFRManual_1, extkey_id = DFRToRemoveLeaf_1)
|
||||
EOF);
|
||||
|
||||
$aClasses = ['DFRManual', 'DFRToRemoveLeaf'];
|
||||
$oService = new StaticDeletionPlan();
|
||||
$aRes = $oService->GetCleanupSummary($aClasses);
|
||||
|
||||
$aExpected = [
|
||||
'DFRToRemoveLeaf' => ['iDeleteCount' => 1],
|
||||
'DFRManual' => ['iDeleteCount' => 1, 'iIssueCount' => 1],
|
||||
];
|
||||
$this->AssertSummaryEquals($aExpected, $aRes);
|
||||
}
|
||||
|
||||
public function testCircularRefsShouldNotRunInfinitely()
|
||||
{
|
||||
$this->GivenDFRObjectsInDB(<<<EOF
|
||||
|
||||
@@ -319,58 +319,6 @@
|
||||
</presentation>
|
||||
<parent>cmdbAbstractObject</parent>
|
||||
</class>
|
||||
<class id="DFRManualNullable" _created_in="itop-structure" _delta="define">
|
||||
<properties>
|
||||
<category>bizmodel,searchable</category>
|
||||
<abstract>false</abstract>
|
||||
<db_table>dfrmanualnullable</db_table>
|
||||
<naming>
|
||||
<attributes/>
|
||||
</naming>
|
||||
<reconciliation>
|
||||
<attributes/>
|
||||
</reconciliation>
|
||||
</properties>
|
||||
<fields>
|
||||
<field id="name" xsi:type="AttributeString">
|
||||
<sql>name</sql>
|
||||
<default_value/>
|
||||
<is_null_allowed>true</is_null_allowed>
|
||||
<validation_pattern/>
|
||||
<dependencies/>
|
||||
<tracking_level>all</tracking_level>
|
||||
</field>
|
||||
<field id="extkey_id" xsi:type="AttributeExternalKey">
|
||||
<sql>extkey_id</sql>
|
||||
<filter/>
|
||||
<dependencies/>
|
||||
<is_null_allowed>true</is_null_allowed>
|
||||
<target_class>DFRToRemove</target_class>
|
||||
<on_target_delete>DEL_MANUAL</on_target_delete>
|
||||
<tracking_level>all</tracking_level>
|
||||
</field>
|
||||
</fields>
|
||||
<methods/>
|
||||
<presentation>
|
||||
<list>
|
||||
<items/>
|
||||
</list>
|
||||
<search>
|
||||
<items/>
|
||||
</search>
|
||||
<details>
|
||||
<items>
|
||||
<item id="name">
|
||||
<rank>10</rank>
|
||||
</item>
|
||||
<item id="extkey_id">
|
||||
<rank>20</rank>
|
||||
</item>
|
||||
</items>
|
||||
</details>
|
||||
</presentation>
|
||||
<parent>cmdbAbstractObject</parent>
|
||||
</class>
|
||||
<class id="DFRLeafNotToRemove" _created_in="itop-structure" _delta="define">
|
||||
<properties>
|
||||
<category>bizmodel,searchable</category>
|
||||
@@ -770,14 +718,6 @@
|
||||
<entry id="Class:DFRManual/Attribute:name+" _delta="define"><![CDATA[]]></entry>
|
||||
<entry id="Class:DFRManual/Attribute:extkey_id" _delta="define"><![CDATA[Dfrtoremove id]]></entry>
|
||||
<entry id="Class:DFRManual/Attribute:extkey_id+" _delta="define"><![CDATA[]]></entry>
|
||||
<entry id="Class:DFRManualNullable/Name" _delta="define"><![CDATA[]]></entry>
|
||||
<entry id="Class:DFRManualNullable/ComplementaryName" _delta="define"><![CDATA[]]></entry>
|
||||
<entry id="Class:DFRManualNullable" _delta="define"><![CDATA[DFRManualNullable]]></entry>
|
||||
<entry id="Class:DFRManualNullable+" _delta="define"><![CDATA[]]></entry>
|
||||
<entry id="Class:DFRManualNullable/Attribute:name" _delta="define"><![CDATA[Name]]></entry>
|
||||
<entry id="Class:DFRManualNullable/Attribute:name+" _delta="define"><![CDATA[]]></entry>
|
||||
<entry id="Class:DFRManualNullable/Attribute:extkey_id" _delta="define"><![CDATA[Dfrtoremove id]]></entry>
|
||||
<entry id="Class:DFRManualNullable/Attribute:extkey_id+" _delta="define"><![CDATA[]]></entry>
|
||||
</entries>
|
||||
</dictionary>
|
||||
</dictionaries>
|
||||
|
||||
@@ -78,6 +78,9 @@ class WizStepModulesChoiceTest extends ItopTestCase
|
||||
],
|
||||
'A missing extension should be disabled and unchecked' => [
|
||||
'aExtensionsOnDiskOrDb' => [
|
||||
'itop-ext1' => [
|
||||
'installed' => false,
|
||||
],
|
||||
],
|
||||
'aWizardStepDefinition' => [
|
||||
'extension_code' => 'itop-ext1',
|
||||
@@ -99,6 +102,9 @@ class WizStepModulesChoiceTest extends ItopTestCase
|
||||
],
|
||||
'A missing extension should always be disabled and unchecked, even when mandatory' => [
|
||||
'aExtensionsOnDiskOrDb' => [
|
||||
'itop-ext1' => [
|
||||
'installed' => false,
|
||||
],
|
||||
],
|
||||
'aWizardStepDefinition' => [
|
||||
'extension_code' => 'itop-ext1',
|
||||
@@ -120,6 +126,9 @@ class WizStepModulesChoiceTest extends ItopTestCase
|
||||
],
|
||||
'A missing extension should always be disabled and unchecked, even when non-uninstallable' => [
|
||||
'aExtensionsOnDiskOrDb' => [
|
||||
'itop-ext1' => [
|
||||
'installed' => false,
|
||||
],
|
||||
],
|
||||
'aWizardStepDefinition' => [
|
||||
'extension_code' => 'itop-ext1',
|
||||
|
||||
@@ -79,7 +79,6 @@ class SetupAuditTest extends ItopCustomDatamodelTestCase
|
||||
"Feature1Module1MyClass",
|
||||
"FinalClassFeature2Module1MyClass",
|
||||
"FinalClassFeature2Module1MyFinalClassFromLocation",
|
||||
"TagSetFieldDataFor_FinalClassFeature2Module1MyFinalClassFromLocation__domains",
|
||||
];
|
||||
$this->assertEqualsCanonicalizing($expected, $oSetupAudit->GetRemovedClasses());
|
||||
|
||||
|
||||
@@ -64,12 +64,6 @@
|
||||
<is_null_allowed>false</is_null_allowed>
|
||||
<validation_pattern/>
|
||||
</field>
|
||||
<field id="domains" xsi:type="AttributeTagSet">
|
||||
<sql>domains</sql>
|
||||
<is_null_allowed>true</is_null_allowed>
|
||||
<tracking_level>all</tracking_level>
|
||||
<max_items>12</max_items>
|
||||
</field>
|
||||
</fields>
|
||||
<methods/>
|
||||
<presentation/>
|
||||
|
||||
@@ -31,12 +31,6 @@
|
||||
<default_value/>
|
||||
<is_null_allowed>false</is_null_allowed>
|
||||
</field>
|
||||
<field id="domains" xsi:type="AttributeTagSet">
|
||||
<sql>domains</sql>
|
||||
<is_null_allowed>true</is_null_allowed>
|
||||
<tracking_level>all</tracking_level>
|
||||
<max_items>12</max_items>
|
||||
</field>
|
||||
</fields>
|
||||
<methods/>
|
||||
<presentation/>
|
||||
|
||||
Reference in New Issue
Block a user