mirror of
https://github.com/Combodo/iTop.git
synced 2026-02-13 07:24:13 +01:00
Deletion: entirely reviewed to take into account the objects being synchronized by an external data source, or the plugins constraints.
SVN:trunk[1194]
This commit is contained in:
@@ -102,15 +102,16 @@ abstract class cmdbAbstractObject extends CMDBObject implements iDisplay
|
|||||||
$oBlock->Display($oPage, -1);
|
$oBlock->Display($oPage, -1);
|
||||||
|
|
||||||
// Master data sources
|
// Master data sources
|
||||||
|
$sSynchroIcon = '';
|
||||||
$oReplicaSet = $this->GetMasterReplica();
|
$oReplicaSet = $this->GetMasterReplica();
|
||||||
$bSynchronized = false;
|
$bSynchronized = false;
|
||||||
$bCreated = false;
|
$oCreatorTask = null;
|
||||||
$bCanBeDeleted = false;
|
$bCanBeDeletedByTask = false;
|
||||||
|
$bCanBeDeletedByUser = true;
|
||||||
$aMasterSources = array();
|
$aMasterSources = array();
|
||||||
if ($oReplicaSet->Count() > 0)
|
if ($oReplicaSet->Count() > 0)
|
||||||
{
|
{
|
||||||
$bSynchronized = true;
|
$bSynchronized = true;
|
||||||
$sTip = "<p>".Dict::S('Core:Synchro:ThisObjectIsSynchronized')."</p>";
|
|
||||||
while($aData = $oReplicaSet->FetchAssoc())
|
while($aData = $oReplicaSet->FetchAssoc())
|
||||||
{
|
{
|
||||||
// Assumption: $aData['datasource'] will not be null because the data source id is always set...
|
// Assumption: $aData['datasource'] will not be null because the data source id is always set...
|
||||||
@@ -122,27 +123,59 @@ abstract class cmdbAbstractObject extends CMDBObject implements iDisplay
|
|||||||
}
|
}
|
||||||
if ($aData['replica']->Get('status_dest_creator') == 1)
|
if ($aData['replica']->Get('status_dest_creator') == 1)
|
||||||
{
|
{
|
||||||
$sTip .= "<p>".Dict::Format('Core:Synchro:TheObjectWasCreatedBy_Source', $sLink)."</p>";
|
$oCreatorTask = $aData['datasource'];
|
||||||
$bCreated = true;
|
$bCreatedByTask = true;
|
||||||
}
|
}
|
||||||
if ($bCreated)
|
else
|
||||||
|
{
|
||||||
|
$bCreatedByTask = false;
|
||||||
|
}
|
||||||
|
if ($bCreatedByTask)
|
||||||
{
|
{
|
||||||
$sDeletePolicy = $aData['datasource']->Get('delete_policy');
|
$sDeletePolicy = $aData['datasource']->Get('delete_policy');
|
||||||
if (($sDeletePolicy == 'delete') || ($sDeletePolicy == 'update_then_delete'))
|
if (($sDeletePolicy == 'delete') || ($sDeletePolicy == 'update_then_delete'))
|
||||||
{
|
{
|
||||||
$bCanBeDeleted = true;
|
$bCanBeDeletedByTask = true;
|
||||||
$sTip .= "<p>".Dict::Format('Core:Synchro:TheObjectCanBeDeletedBy_Source', $sLink)."</p>";
|
}
|
||||||
|
$sUserDeletePolicy = $aData['datasource']->Get('user_delete_policy');
|
||||||
|
if ($sUserDeletePolicy == 'nobody')
|
||||||
|
{
|
||||||
|
$bCanBeDeletedByUser = false;
|
||||||
|
}
|
||||||
|
elseif (($sUserDeletePolicy == 'administrators') && !UserRights::IsAdministrator())
|
||||||
|
{
|
||||||
|
$bCanBeDeletedByUser = false;
|
||||||
|
}
|
||||||
|
else // everybody...
|
||||||
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$aMasterSources[$aData['datasource']->GetKey()]['datasource'] = $aData['datasource'];
|
$aMasterSources[$aData['datasource']->GetKey()]['datasource'] = $aData['datasource'];
|
||||||
$aMasterSources[$aData['datasource']->GetKey()]['url'] = $sLink;
|
$aMasterSources[$aData['datasource']->GetKey()]['url'] = $sLink;
|
||||||
$aMasterSources[$aData['datasource']->GetKey()]['last_synchro'] = $aData['replica']->Get('status_last_seen');
|
$aMasterSources[$aData['datasource']->GetKey()]['last_synchro'] = $aData['replica']->Get('status_last_seen');
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
if (is_object($oCreatorTask))
|
||||||
$sSynchroIcon = '';
|
{
|
||||||
if ($bSynchronized)
|
$sTaskUrl = $aMasterSources[$oCreatorTask->GetKey()]['url'];
|
||||||
{
|
if (!$bCanBeDeletedByUser)
|
||||||
|
{
|
||||||
|
$sTip = "<p>".Dict::Format('Core:Synchro:TheObjectCannotBeDeletedByUser_Source', $sTaskUrl)."</p>";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$sTip = "<p>".Dict::Format('Core:Synchro:TheObjectWasCreatedBy_Source', $sTaskUrl)."</p>";
|
||||||
|
}
|
||||||
|
if ($bCanBeDeletedByTask)
|
||||||
|
{
|
||||||
|
$sTip .= "<p>".Dict::Format('Core:Synchro:TheObjectCanBeDeletedBy_Source', $sTaskUrl)."</p>";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$sTip = "<p>".Dict::S('Core:Synchro:ThisObjectIsSynchronized')."</p>";
|
||||||
|
}
|
||||||
|
|
||||||
$sTip .= "<p><b>".Dict::S('Core:Synchro:ListOfDataSources')."</b></p>";
|
$sTip .= "<p><b>".Dict::S('Core:Synchro:ListOfDataSources')."</b></p>";
|
||||||
foreach($aMasterSources as $aStruct)
|
foreach($aMasterSources as $aStruct)
|
||||||
{
|
{
|
||||||
@@ -2203,7 +2236,7 @@ EOF
|
|||||||
return parent::BulkUpdateTracked_Internal($oFilter, $aValues);
|
return parent::BulkUpdateTracked_Internal($oFilter, $aValues);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function DBDeleteTracked_Internal()
|
protected function DBDeleteTracked_Internal(&$oDeletionPlan = null)
|
||||||
{
|
{
|
||||||
// Invoke extensions before the deletion (the deletion will do some cleanup and we might loose some information
|
// Invoke extensions before the deletion (the deletion will do some cleanup and we might loose some information
|
||||||
foreach (MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance)
|
foreach (MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance)
|
||||||
@@ -2211,7 +2244,7 @@ EOF
|
|||||||
$oExtensionInstance->OnDBDelete($this, self::$m_oCurrChange);
|
$oExtensionInstance->OnDBDelete($this, self::$m_oCurrChange);
|
||||||
}
|
}
|
||||||
|
|
||||||
return parent::DBDeleteTracked_Internal();
|
return parent::DBDeleteTracked_Internal($oDeletionPlan);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static function BulkDeleteTracked_Internal(DBObjectSearch $oFilter)
|
protected static function BulkDeleteTracked_Internal(DBObjectSearch $oFilter)
|
||||||
@@ -2223,6 +2256,9 @@ EOF
|
|||||||
public function DoCheckToWrite()
|
public function DoCheckToWrite()
|
||||||
{
|
{
|
||||||
parent::DoCheckToWrite();
|
parent::DoCheckToWrite();
|
||||||
|
|
||||||
|
// Plugins
|
||||||
|
//
|
||||||
foreach (MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance)
|
foreach (MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance)
|
||||||
{
|
{
|
||||||
$aNewIssues = $oExtensionInstance->OnCheckToWrite($this);
|
$aNewIssues = $oExtensionInstance->OnCheckToWrite($this);
|
||||||
@@ -2231,11 +2267,37 @@ EOF
|
|||||||
$this->m_aCheckIssues = array_merge($this->m_aCheckIssues, $aNewIssues);
|
$this->m_aCheckIssues = array_merge($this->m_aCheckIssues, $aNewIssues);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// User rights
|
||||||
|
//
|
||||||
|
$aChanges = $this->ListChanges();
|
||||||
|
if (count($aChanges) > 0)
|
||||||
|
{
|
||||||
|
$aForbiddenFields = array();
|
||||||
|
foreach ($this->ListChanges() as $sAttCode => $value)
|
||||||
|
{
|
||||||
|
$bUpdateAllowed = UserRights::IsActionAllowedOnAttribute(get_class($this), $sAttCode, UR_ACTION_MODIFY, DBObjectSet::FromObject($this));
|
||||||
|
if (!$bUpdateAllowed)
|
||||||
|
{
|
||||||
|
$oAttCode = MetaModel::GetAttributeDef(get_class($this), $sAttCode);
|
||||||
|
$aForbiddenFields[] = $oAttCode->GetLabel();
|
||||||
|
}
|
||||||
|
if (count($aForbiddenFields) > 0)
|
||||||
|
{
|
||||||
|
// Security issue
|
||||||
|
$this->m_bSecurityIssue = true;
|
||||||
|
$this->m_aCheckIssues[] = Dict::Format('UI:Delete:NotAllowedToUpdate_Fields',implode(', ', $aForbiddenFields));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function DoCheckToDelete()
|
protected function DoCheckToDelete()
|
||||||
{
|
{
|
||||||
parent::DoCheckToDelete();
|
parent::DoCheckToDelete();
|
||||||
|
|
||||||
|
// Plugins
|
||||||
|
//
|
||||||
foreach (MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance)
|
foreach (MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance)
|
||||||
{
|
{
|
||||||
$aNewIssues = $oExtensionInstance->OnCheckToDelete($this);
|
$aNewIssues = $oExtensionInstance->OnCheckToDelete($this);
|
||||||
@@ -2244,6 +2306,16 @@ EOF
|
|||||||
$this->m_aDeleteIssues = array_merge($this->m_aDeleteIssues, $aNewIssues);
|
$this->m_aDeleteIssues = array_merge($this->m_aDeleteIssues, $aNewIssues);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// User rights
|
||||||
|
//
|
||||||
|
$bDeleteAllowed = UserRights::IsActionAllowed(get_class($this), UR_ACTION_DELETE, DBObjectSet::FromObject($this));
|
||||||
|
if (!$bDeleteAllowed)
|
||||||
|
{
|
||||||
|
// Security issue
|
||||||
|
$this->m_bSecurityIssue = true;
|
||||||
|
$this->m_aDeleteIssues[] = Dict::S('UI:Delete:NotAllowedToDelete');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -169,9 +169,9 @@ class appUserPreferences extends DBObject
|
|||||||
* Overloading this function here to secure a fix done right before the release
|
* Overloading this function here to secure a fix done right before the release
|
||||||
* The real fix should be to implement this verb in DBObject
|
* The real fix should be to implement this verb in DBObject
|
||||||
*/
|
*/
|
||||||
public function DBDeleteTracked(CMDBChange $oChange, $bSkipStrongSecurity = null)
|
public function DBDeleteTracked(CMDBChange $oChange, $bSkipStrongSecurity = null, &$oDeletionPlan = null)
|
||||||
{
|
{
|
||||||
$this->DBDelete();
|
$this->DBDelete($oDeletionPlan);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
?>
|
?>
|
||||||
|
|||||||
@@ -620,7 +620,14 @@ class BulkChange
|
|||||||
//
|
//
|
||||||
if ($oChange)
|
if ($oChange)
|
||||||
{
|
{
|
||||||
$oTargetObj->DBUpdateTracked($oChange);
|
try
|
||||||
|
{
|
||||||
|
$oTargetObj->DBUpdateTracked($oChange);
|
||||||
|
}
|
||||||
|
catch(CoreException $e)
|
||||||
|
{
|
||||||
|
$aResult[$iRow]["__STATUS__"] = new RowStatus_Issue($e->getMessage());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -649,12 +656,19 @@ class BulkChange
|
|||||||
if (count($aChangedFields) > 0)
|
if (count($aChangedFields) > 0)
|
||||||
{
|
{
|
||||||
$aResult[$iRow]["__STATUS__"] = new RowStatus_Disappeared(count($aChangedFields));
|
$aResult[$iRow]["__STATUS__"] = new RowStatus_Disappeared(count($aChangedFields));
|
||||||
|
|
||||||
// Optionaly record the results
|
// Optionaly record the results
|
||||||
//
|
//
|
||||||
if ($oChange)
|
if ($oChange)
|
||||||
{
|
{
|
||||||
$oTargetObj->DBUpdateTracked($oChange);
|
try
|
||||||
|
{
|
||||||
|
$oTargetObj->DBUpdateTracked($oChange);
|
||||||
|
}
|
||||||
|
catch(CoreException $e)
|
||||||
|
{
|
||||||
|
$aResult[$iRow]["__STATUS__"] = new RowStatus_Issue($e->getMessage());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -369,28 +369,28 @@ abstract class CMDBObject extends DBObject
|
|||||||
return $ret;
|
return $ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function DBDelete()
|
public function DBDelete(&$oDeletionPlan = null)
|
||||||
{
|
{
|
||||||
if(!self::$m_oCurrChange)
|
if(!self::$m_oCurrChange)
|
||||||
{
|
{
|
||||||
throw new CoreException("DBDelete() could not be used here, please use DBDeleteTracked() instead");
|
throw new CoreException("DBDelete() could not be used here, please use DBDeleteTracked() instead");
|
||||||
}
|
}
|
||||||
return $this->DBDeleteTracked_Internal();
|
return $this->DBDeleteTracked_Internal($oDeletionPlan);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function DBDeleteTracked(CMDBChange $oChange, $bSkipStrongSecurity = null)
|
public function DBDeleteTracked(CMDBChange $oChange, $bSkipStrongSecurity = null, &$oDeletionPlan = null)
|
||||||
{
|
{
|
||||||
$this->CheckUserRights($bSkipStrongSecurity, UR_ACTION_DELETE);
|
$this->CheckUserRights($bSkipStrongSecurity, UR_ACTION_DELETE);
|
||||||
|
|
||||||
self::$m_oCurrChange = $oChange;
|
self::$m_oCurrChange = $oChange;
|
||||||
$this->DBDeleteTracked_Internal();
|
$this->DBDeleteTracked_Internal($oDeletionPlan);
|
||||||
self::$m_oCurrChange = null;
|
self::$m_oCurrChange = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function DBDeleteTracked_Internal()
|
protected function DBDeleteTracked_Internal(&$oDeletionPlan = null)
|
||||||
{
|
{
|
||||||
$prevkey = $this->GetKey();
|
$prevkey = $this->GetKey();
|
||||||
$ret = parent::DBDelete();
|
$ret = parent::DBDelete($oDeletionPlan);
|
||||||
$this->RecordObjDeletion(self::$m_oCurrChange, $prevkey);
|
$this->RecordObjDeletion(self::$m_oCurrChange, $prevkey);
|
||||||
return $ret;
|
return $ret;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,8 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
require_once('metamodel.class.php');
|
require_once('metamodel.class.php');
|
||||||
|
require_once('deletionplan.class.inc.php');
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A persistent object, as defined by the metamodel
|
* A persistent object, as defined by the metamodel
|
||||||
@@ -49,6 +51,7 @@ abstract class DBObject
|
|||||||
// The object may have incorrect external keys, then any attempt of reload must be avoided
|
// The object may have incorrect external keys, then any attempt of reload must be avoided
|
||||||
private $m_bCheckStatus = null; // Means: the object has been verified and is consistent with integrity rules
|
private $m_bCheckStatus = null; // Means: the object has been verified and is consistent with integrity rules
|
||||||
// if null, then the check has to be performed again to know the status
|
// if null, then the check has to be performed again to know the status
|
||||||
|
protected $m_bSecurityIssue = null;
|
||||||
protected $m_aCheckIssues = null;
|
protected $m_aCheckIssues = null;
|
||||||
protected $m_aDeleteIssues = null;
|
protected $m_aDeleteIssues = null;
|
||||||
|
|
||||||
@@ -153,7 +156,7 @@ abstract class DBObject
|
|||||||
protected function Reload()
|
protected function Reload()
|
||||||
{
|
{
|
||||||
assert($this->m_bIsInDB);
|
assert($this->m_bIsInDB);
|
||||||
$aRow = MetaModel::MakeSingleRow(get_class($this), $this->m_iKey);
|
$aRow = MetaModel::MakeSingleRow(get_class($this), $this->m_iKey, false/*, $this->m_bAllowAllData*/);
|
||||||
if (empty($aRow))
|
if (empty($aRow))
|
||||||
{
|
{
|
||||||
throw new CoreException("Failed to reload object of class '".get_class($this)."', id = ".$this->m_iKey);
|
throw new CoreException("Failed to reload object of class '".get_class($this)."', id = ".$this->m_iKey);
|
||||||
@@ -671,7 +674,7 @@ abstract class DBObject
|
|||||||
* @param string $sAttCode The code of the attribute
|
* @param string $sAttCode The code of the attribute
|
||||||
* @return integer Flags: the binary combination of the flags applicable to this attribute
|
* @return integer Flags: the binary combination of the flags applicable to this attribute
|
||||||
*/
|
*/
|
||||||
public function GetAttributeFlags($sAttCode)
|
public function GetAttributeFlags($sAttCode, &$aReasons = array())
|
||||||
{
|
{
|
||||||
$iFlags = 0; // By default (if no life cycle) no flag at all
|
$iFlags = 0; // By default (if no life cycle) no flag at all
|
||||||
$sStateAttCode = MetaModel::GetStateAttributeCode(get_class($this));
|
$sStateAttCode = MetaModel::GetStateAttributeCode(get_class($this));
|
||||||
@@ -765,6 +768,7 @@ abstract class DBObject
|
|||||||
$this->DoComputeValues();
|
$this->DoComputeValues();
|
||||||
|
|
||||||
$this->m_aCheckIssues = array();
|
$this->m_aCheckIssues = array();
|
||||||
|
$aChanges = $this->ListChanges();
|
||||||
|
|
||||||
foreach(MetaModel::ListAttributeDefs(get_class($this)) as $sAttCode=>$oAttDef)
|
foreach(MetaModel::ListAttributeDefs(get_class($this)) as $sAttCode=>$oAttDef)
|
||||||
{
|
{
|
||||||
@@ -787,6 +791,27 @@ abstract class DBObject
|
|||||||
// $res contains the error description
|
// $res contains the error description
|
||||||
$this->m_aCheckIssues[] = "Consistency rules not followed: $res";
|
$this->m_aCheckIssues[] = "Consistency rules not followed: $res";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Synchronization: are we attempting to modify an attribute for which an external source is master?
|
||||||
|
//
|
||||||
|
if ($this->m_bIsInDB && $this->InSyncScope() && (count($aChanges) > 0))
|
||||||
|
{
|
||||||
|
foreach($aChanges as $sAttCode => $value)
|
||||||
|
{
|
||||||
|
$iFlags = $this->GetSynchroReplicaFlags($sAttCode, $aReasons);
|
||||||
|
if ($iFlags & OPT_ATT_SLAVE)
|
||||||
|
{
|
||||||
|
// Note: $aReasonInfo['name'] could be reported (the task owning the attribute)
|
||||||
|
$oAttDef = MetaModel::GetAttributeDef(get_class($this), $sAttCode);
|
||||||
|
$sAttLabel = $oAttDef->GetLabel();
|
||||||
|
foreach($aReasons as $aReasonInfo)
|
||||||
|
{
|
||||||
|
// Todo: associate the attribute code with the error
|
||||||
|
$this->m_aCheckIssues[] = Dict::Format('UI:AttemptingToSetASlaveAttribute_Name', $sAttLabel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final public function CheckToWrite()
|
final public function CheckToWrite()
|
||||||
@@ -809,7 +834,7 @@ abstract class DBObject
|
|||||||
$this->m_bCheckStatus = false;
|
$this->m_bCheckStatus = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return array($this->m_bCheckStatus, $this->m_aCheckIssues);
|
return array($this->m_bCheckStatus, $this->m_aCheckIssues, $this->m_bSecurityIssue);
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if it is allowed to delete the existing object from the database
|
// check if it is allowed to delete the existing object from the database
|
||||||
@@ -817,10 +842,57 @@ abstract class DBObject
|
|||||||
protected function DoCheckToDelete()
|
protected function DoCheckToDelete()
|
||||||
{
|
{
|
||||||
$this->m_aDeleteIssues = array(); // Ok
|
$this->m_aDeleteIssues = array(); // Ok
|
||||||
|
|
||||||
|
if ($this->InSyncScope())
|
||||||
|
{
|
||||||
|
$oReplicaSet = $this->GetMasterReplica();
|
||||||
|
if ($oReplicaSet->Count() > 0)
|
||||||
|
{
|
||||||
|
while($aData = $oReplicaSet->FetchAssoc())
|
||||||
|
{
|
||||||
|
if ($aData['datasource']->GetKey() == SynchroDataSource::GetCurrentTaskId())
|
||||||
|
{
|
||||||
|
// The current task has the right to delete the object
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($aData['replica']->Get('status_dest_creator') != 1)
|
||||||
|
{
|
||||||
|
// The object is not owned by the task
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$sLink = $aData['datasource']->GetName();
|
||||||
|
$sUserDeletePolicy = $aData['datasource']->Get('user_delete_policy');
|
||||||
|
switch($sUserDeletePolicy)
|
||||||
|
{
|
||||||
|
case 'nobody':
|
||||||
|
$this->m_aDeleteIssues[] = Dict::Format('Core:Synchro:TheObjectCannotBeDeletedByUser_Source', $sLink);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'administrators':
|
||||||
|
if (!UserRights::IsAdministrator())
|
||||||
|
{
|
||||||
|
$this->m_aDeleteIssues[] = Dict::Format('Core:Synchro:TheObjectCannotBeDeletedByUser_Source', $sLink);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'everybody':
|
||||||
|
default:
|
||||||
|
// Ok
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// final public function CheckToDelete() - THE EQUIVALENT OF CheckToWrite IS NOT AVAILABLE
|
final public function CheckToDelete(&$oDeletionPlan)
|
||||||
// Todo - split the "DeleteObject()" function (UI.php) and move the generic part in cmdbAbstractObject, etc.
|
{
|
||||||
|
$this->MakeDeletionPlan($oDeletionPlan);
|
||||||
|
$oDeletionPlan->ComputeResults();
|
||||||
|
return (!$oDeletionPlan->FoundStopper());
|
||||||
|
}
|
||||||
|
|
||||||
protected function ListChangedValues(array $aProposal)
|
protected function ListChangedValues(array $aProposal)
|
||||||
{
|
{
|
||||||
@@ -1079,7 +1151,8 @@ abstract class DBObject
|
|||||||
list($bRes, $aIssues) = $this->CheckToWrite();
|
list($bRes, $aIssues) = $this->CheckToWrite();
|
||||||
if (!$bRes)
|
if (!$bRes)
|
||||||
{
|
{
|
||||||
throw new CoreException("Object not following integrity rules - it will not be written into the DB", array('class' => $sClass, 'id' => $this->GetKey(), 'issues' => $aIssues));
|
$sIssues = implode(', ', $aIssues);
|
||||||
|
throw new CoreException("Object not following integrity rules", array('issues' => $sIssues, 'class' => get_class($this), 'id' => $this->GetKey()));
|
||||||
}
|
}
|
||||||
|
|
||||||
// First query built upon on the root class, because the ID must be created first
|
// First query built upon on the root class, because the ID must be created first
|
||||||
@@ -1176,7 +1249,8 @@ abstract class DBObject
|
|||||||
list($bRes, $aIssues) = $this->CheckToWrite();
|
list($bRes, $aIssues) = $this->CheckToWrite();
|
||||||
if (!$bRes)
|
if (!$bRes)
|
||||||
{
|
{
|
||||||
throw new CoreException("Object not following integrity rules - it will not be written into the DB", array('class' => get_class($this), 'id' => $this->GetKey(), 'issues' => $aIssues));
|
$sIssues = implode(', ', $aIssues);
|
||||||
|
throw new CoreException("Object not following integrity rules", array('issues' => $sIssues, 'class' => get_class($this), 'id' => $this->GetKey()));
|
||||||
}
|
}
|
||||||
|
|
||||||
$bHasANewExternalKeyValue = false;
|
$bHasANewExternalKeyValue = false;
|
||||||
@@ -1245,24 +1319,16 @@ abstract class DBObject
|
|||||||
CMDBSource::DeleteFrom($sDeleteSQL);
|
CMDBSource::DeleteFrom($sDeleteSQL);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function DBDeleteInternal()
|
private function DBDeleteSingleObject()
|
||||||
{
|
|
||||||
$sClass = get_class($this);
|
|
||||||
|
|
||||||
foreach(MetaModel::EnumParentClasses($sClass, ENUM_PARENT_CLASSES_ALL) as $sParentClass)
|
|
||||||
{
|
|
||||||
$this->DBDeleteSingleTable($sParentClass);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete a record
|
|
||||||
public function DBDelete()
|
|
||||||
{
|
{
|
||||||
$this->OnDelete();
|
$this->OnDelete();
|
||||||
|
|
||||||
if (!MetaModel::DBIsReadOnly())
|
if (!MetaModel::DBIsReadOnly())
|
||||||
{
|
{
|
||||||
$this->DBDeleteInternal();
|
foreach(MetaModel::EnumParentClasses(get_class($this), ENUM_PARENT_CLASSES_ALL) as $sParentClass)
|
||||||
|
{
|
||||||
|
$this->DBDeleteSingleTable($sParentClass);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->AfterDelete();
|
$this->AfterDelete();
|
||||||
@@ -1271,9 +1337,53 @@ abstract class DBObject
|
|||||||
$this->m_iKey = null;
|
$this->m_iKey = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function DBDeleteTracked(CMDBChange $oVoid)
|
// Delete an object... and guarantee data integrity
|
||||||
|
//
|
||||||
|
public function DBDelete(&$oDeletionPlan = null)
|
||||||
{
|
{
|
||||||
$this->DBDelete();
|
if (is_null($oDeletionPlan))
|
||||||
|
{
|
||||||
|
$oDeletionPlan = new DeletionPlan();
|
||||||
|
}
|
||||||
|
$this->MakeDeletionPlan($oDeletionPlan);
|
||||||
|
$oDeletionPlan->ComputeResults();
|
||||||
|
|
||||||
|
if ($oDeletionPlan->FoundStopper())
|
||||||
|
{
|
||||||
|
$aIssues = $oDeletionPlan->GetIssues();
|
||||||
|
throw new DeleteException('Found issue(s)', array('target_class' => get_class($this), 'target_id' => $this->GetKey(), 'issues' => implode(', ', $aIssues)));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
foreach ($oDeletionPlan->ListDeletes() as $sClass => $aToDelete)
|
||||||
|
{
|
||||||
|
foreach ($aToDelete as $iId => $aData)
|
||||||
|
{
|
||||||
|
$oToDelete = $aData['to_delete'];
|
||||||
|
$oToDelete->DBDeleteSingleObject();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($oDeletionPlan->ListUpdates() as $sClass => $aToUpdate)
|
||||||
|
{
|
||||||
|
foreach ($aToUpdate as $iId => $aData)
|
||||||
|
{
|
||||||
|
$oToUpdate = $aData['to_reset'];
|
||||||
|
foreach ($aData['attributes'] as $sRemoteExtKey => $aRemoteAttDef)
|
||||||
|
{
|
||||||
|
$oToUpdate->Set($sRemoteExtKey, 0);
|
||||||
|
$oToUpdate->DBUpdate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $oDeletionPlan;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function DBDeleteTracked(CMDBChange $oVoid, $bSkipStrongSecurity = null, &$oDeletionPlan = null)
|
||||||
|
{
|
||||||
|
$this->DBDelete($oDeletionPlan);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function EnumTransitions()
|
public function EnumTransitions()
|
||||||
@@ -1447,7 +1557,7 @@ abstract class DBObject
|
|||||||
return $aResults;
|
return $aResults;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function GetReferencingObjects()
|
public function GetReferencingObjects($bAllowAllData = false)
|
||||||
{
|
{
|
||||||
$aDependentObjects = array();
|
$aDependentObjects = array();
|
||||||
$aRererencingMe = MetaModel::EnumReferencingClasses(get_class($this));
|
$aRererencingMe = MetaModel::EnumReferencingClasses(get_class($this));
|
||||||
@@ -1460,6 +1570,10 @@ abstract class DBObject
|
|||||||
|
|
||||||
$oSearch = new DBObjectSearch($sRemoteClass);
|
$oSearch = new DBObjectSearch($sRemoteClass);
|
||||||
$oSearch->AddCondition($sExtKeyAttCode, $this->GetKey(), '=');
|
$oSearch->AddCondition($sExtKeyAttCode, $this->GetKey(), '=');
|
||||||
|
if ($bAllowAllData)
|
||||||
|
{
|
||||||
|
$oSearch->AllowAllData();
|
||||||
|
}
|
||||||
$oSet = new CMDBObjectSet($oSearch);
|
$oSet = new CMDBObjectSet($oSearch);
|
||||||
if ($oSet->Count() > 0)
|
if ($oSet->Count() > 0)
|
||||||
{
|
{
|
||||||
@@ -1473,15 +1587,13 @@ abstract class DBObject
|
|||||||
return $aDependentObjects;
|
return $aDependentObjects;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private function MakeDeletionPlan(&$oDeletionPlan, $aVisited = array(), $iDeleteOption = null)
|
||||||
* $aDeletedObjs = array(); // [class][key] => structure
|
|
||||||
* $aResetedObjs = array(); // [class][key] => object
|
|
||||||
*/
|
|
||||||
public function GetDeletionScheme(&$aDeletedObjs, &$aResetedObjs, $aVisited = array())
|
|
||||||
{
|
{
|
||||||
$sClass = get_class($this);
|
$sClass = get_class($this);
|
||||||
$iThisId = $this->GetKey();
|
$iThisId = $this->GetKey();
|
||||||
|
|
||||||
|
$iDeleteOption = $oDeletionPlan->AddToDelete($this, $iDeleteOption);
|
||||||
|
|
||||||
if (array_key_exists($sClass, $aVisited))
|
if (array_key_exists($sClass, $aVisited))
|
||||||
{
|
{
|
||||||
if (in_array($iThisId, $aVisited[$sClass]))
|
if (in_array($iThisId, $aVisited[$sClass]))
|
||||||
@@ -1491,13 +1603,16 @@ abstract class DBObject
|
|||||||
}
|
}
|
||||||
$aVisited[$sClass] = $iThisId;
|
$aVisited[$sClass] = $iThisId;
|
||||||
|
|
||||||
$aDeletedObjs[$sClass][$iThisId]['to_delete'] = $this;
|
if ($iDeleteOption == DEL_MANUAL)
|
||||||
$aDeletedObjs[$sClass][$iThisId]['auto_delete'] = true;
|
{
|
||||||
|
// Stop the recursion here
|
||||||
|
return;
|
||||||
|
}
|
||||||
// Check the node itself
|
// Check the node itself
|
||||||
$this->DoCheckToDelete();
|
$this->DoCheckToDelete();
|
||||||
$aDeletedObjs[$sClass][$iThisId]['issues'] = $this->m_aDeleteIssues;
|
$oDeletionPlan->SetDeletionIssues($this, $this->m_aDeleteIssues, $this->m_bSecurityIssue);
|
||||||
|
|
||||||
$aDependentObjects = $this->GetReferencingObjects();
|
$aDependentObjects = $this->GetReferencingObjects(true /* allow all data */);
|
||||||
foreach ($aDependentObjects as $sRemoteClass => $aPotentialDeletes)
|
foreach ($aDependentObjects as $sRemoteClass => $aPotentialDeletes)
|
||||||
{
|
{
|
||||||
foreach ($aPotentialDeletes as $sRemoteExtKey => $aData)
|
foreach ($aPotentialDeletes as $sRemoteExtKey => $aData)
|
||||||
@@ -1512,44 +1627,12 @@ abstract class DBObject
|
|||||||
if ($oAttDef->IsNullAllowed())
|
if ($oAttDef->IsNullAllowed())
|
||||||
{
|
{
|
||||||
// Optional external key, list to reset
|
// Optional external key, list to reset
|
||||||
if (!array_key_exists($sRemoteClass, $aResetedObjs) || !array_key_exists($iId, $aResetedObjs[$sRemoteClass]))
|
$oDeletionPlan->AddToUpdate($oDependentObj, $oAttDef);
|
||||||
{
|
|
||||||
$aResetedObjs[$sRemoteClass][$iId]['to_reset'] = $oDependentObj;
|
|
||||||
}
|
|
||||||
$aResetedObjs[$sRemoteClass][$iId]['attributes'][$sRemoteExtKey] = $oAttDef;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Mandatory external key, list to delete
|
// Mandatory external key, list to delete
|
||||||
if (array_key_exists($sRemoteClass, $aDeletedObjs) && array_key_exists($iId, $aDeletedObjs[$sRemoteClass]))
|
$oDependentObj->MakeDeletionPlan($oDeletionPlan, $aVisited, $iDeletePropagationOption);
|
||||||
{
|
|
||||||
$iCurrentOption = $aDeletedObjs[$sRemoteClass][$iId];
|
|
||||||
if ($iCurrentOption == DEL_AUTO)
|
|
||||||
{
|
|
||||||
// be conservative, take the new option
|
|
||||||
// (DEL_MANUAL has precedence over DEL_AUTO)
|
|
||||||
$aDeletedObjs[$sRemoteClass][$iId]['auto_delete'] = ($iDeletePropagationOption == DEL_AUTO);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// DEL_MANUAL... leave it as is, it HAS to be verified anyway
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// First time we find the given object in the list
|
|
||||||
// (and most likely case is that no other occurence will be found)
|
|
||||||
if ($iDeletePropagationOption == DEL_AUTO)
|
|
||||||
{
|
|
||||||
// Recursively inspect this object
|
|
||||||
$oDependentObj->GetDeletionScheme($aDeletedObjs, $aResetedObjs, $aVisited);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$aDeletedObjs[$sRemoteClass][$iId]['to_delete'] = $oDependentObj;
|
|
||||||
$aDeletedObjs[$sRemoteClass][$iId]['auto_delete'] = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1565,10 +1648,10 @@ abstract class DBObject
|
|||||||
{
|
{
|
||||||
if ($this->m_oMasterReplicaSet == null)
|
if ($this->m_oMasterReplicaSet == null)
|
||||||
{
|
{
|
||||||
$aParentClasses = MetaModel::EnumParentClasses(get_class($this), ENUM_PARENT_CLASSES_ALL);
|
//$aParentClasses = MetaModel::EnumParentClasses(get_class($this), ENUM_PARENT_CLASSES_ALL);
|
||||||
$sClassesList = "'".implode("','", $aParentClasses)."'";
|
//$sClassesList = "'".implode("','", $aParentClasses)."'";
|
||||||
$sOQL = "SELECT replica,datasource FROM SynchroReplica AS replica JOIN SynchroDataSource AS datasource ON replica.sync_source_id=datasource.id WHERE datasource.scope_class IN ($sClassesList) AND replica.dest_id = :dest_id";
|
$sOQL = "SELECT replica,datasource FROM SynchroReplica AS replica JOIN SynchroDataSource AS datasource ON replica.sync_source_id=datasource.id WHERE replica.dest_class = :dest_class AND replica.dest_id = :dest_id";
|
||||||
$oReplicaSet = new DBObjectSet(DBObjectSearch::FromOQL($sOQL), array() /* order by*/, array('dest_id' => $this->GetKey()));
|
$oReplicaSet = new DBObjectSet(DBObjectSearch::FromOQL($sOQL), array() /* order by*/, array('dest_class' => get_class($this), 'dest_id' => $this->GetKey()));
|
||||||
$this->m_oMasterReplicaSet = $oReplicaSet;
|
$this->m_oMasterReplicaSet = $oReplicaSet;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -1584,6 +1667,11 @@ abstract class DBObject
|
|||||||
$oSet = $this->GetMasterReplica();
|
$oSet = $this->GetMasterReplica();
|
||||||
while($aData = $oSet->FetchAssoc())
|
while($aData = $oSet->FetchAssoc())
|
||||||
{
|
{
|
||||||
|
if ($aData['datasource']->GetKey() == SynchroDataSource::GetCurrentTaskId())
|
||||||
|
{
|
||||||
|
// Ignore the current task (check to write => ok)
|
||||||
|
continue;
|
||||||
|
}
|
||||||
// Assumption: $aData['datasource'] will not be null because the data source id is always set...
|
// Assumption: $aData['datasource'] will not be null because the data source id is always set...
|
||||||
$oReplica = $aData['replica'];
|
$oReplica = $aData['replica'];
|
||||||
$oSource = $aData['datasource'];
|
$oSource = $aData['datasource'];
|
||||||
@@ -1600,6 +1688,29 @@ abstract class DBObject
|
|||||||
}
|
}
|
||||||
return $iFlags;
|
return $iFlags;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function InSyncScope()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// TODO - FINALIZE THIS OPTIMIZATION
|
||||||
|
//
|
||||||
|
// Optimization: cache the list of Data Sources and classes candidates for synchro
|
||||||
|
//
|
||||||
|
static $aSynchroClasses = null;
|
||||||
|
if (is_null($aSynchroClasses))
|
||||||
|
{
|
||||||
|
$aSynchroClasses = array();
|
||||||
|
$sOQL = "SELECT SynchroDataSource AS datasource";
|
||||||
|
$oSourceSet = new DBObjectSet(DBObjectSearch::FromOQL($sOQL), array() /* order by*/, array());
|
||||||
|
while($oSource = $oSourceSet->Fetch())
|
||||||
|
{
|
||||||
|
$sTarget = $oSource->Get('scope_class');
|
||||||
|
$aSynchroClasses[] = $oSource;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// to be continued...
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
287
core/deletionplan.class.inc.php
Normal file
287
core/deletionplan.class.inc.php
Normal file
@@ -0,0 +1,287 @@
|
|||||||
|
<?php
|
||||||
|
// Copyright (C) 2010 Combodo SARL
|
||||||
|
//
|
||||||
|
// This program is free software; you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation; version 3 of the License.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with this program; if not, write to the Free Software
|
||||||
|
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class dbObject: the root of persistent classes
|
||||||
|
*
|
||||||
|
* @author Erwan Taloc <erwan.taloc@combodo.com>
|
||||||
|
* @author Romain Quetiez <romain.quetiez@combodo.com>
|
||||||
|
* @author Denis Flaven <denis.flaven@combodo.com>
|
||||||
|
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
|
||||||
|
*/
|
||||||
|
|
||||||
|
class DeleteException extends CoreException
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletion plan (other objects to be deleted/modified, eventual issues, etc.)
|
||||||
|
*
|
||||||
|
* @package iTopORM
|
||||||
|
*/
|
||||||
|
class DeletionPlan
|
||||||
|
{
|
||||||
|
//protected $m_aIssues;
|
||||||
|
|
||||||
|
protected $m_bFoundStopper;
|
||||||
|
protected $m_bFoundSecurityIssue;
|
||||||
|
protected $m_bFoundManualDelete;
|
||||||
|
protected $m_bFoundManualOperation;
|
||||||
|
|
||||||
|
protected $m_iToDelete;
|
||||||
|
protected $m_iToUpdate;
|
||||||
|
|
||||||
|
protected $m_aToDelete;
|
||||||
|
protected $m_aToUpdate;
|
||||||
|
|
||||||
|
protected static $m_aModeUpdate = array(
|
||||||
|
DEL_SILENT => array(
|
||||||
|
DEL_SILENT => DEL_SILENT,
|
||||||
|
DEL_AUTO => DEL_AUTO,
|
||||||
|
DEL_MANUAL => DEL_MANUAL
|
||||||
|
),
|
||||||
|
DEL_MANUAL => array(
|
||||||
|
DEL_SILENT => DEL_MANUAL,
|
||||||
|
DEL_AUTO => DEL_AUTO,
|
||||||
|
DEL_MANUAL => DEL_MANUAL
|
||||||
|
),
|
||||||
|
DEL_AUTO => array(
|
||||||
|
DEL_SILENT => DEL_AUTO,
|
||||||
|
DEL_AUTO => DEL_AUTO,
|
||||||
|
DEL_MANUAL => DEL_AUTO
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->m_iToDelete = 0;
|
||||||
|
$this->m_iToUpdate = 0;
|
||||||
|
|
||||||
|
$this->m_aToDelete = array();
|
||||||
|
$this->m_aToUpdate = array();
|
||||||
|
|
||||||
|
$this->m_bFoundStopper = false;
|
||||||
|
$this->m_bFoundSecurityIssue = false;
|
||||||
|
$this->m_bFoundManualDelete = false;
|
||||||
|
$this->m_bFoundManualOperation = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function ComputeResults()
|
||||||
|
{
|
||||||
|
foreach($this->m_aToDelete as $sClass => $aToDelete)
|
||||||
|
{
|
||||||
|
foreach($aToDelete as $iId => $aData)
|
||||||
|
{
|
||||||
|
$this->m_iToDelete++;
|
||||||
|
if (isset($aData['issue']))
|
||||||
|
{
|
||||||
|
$this->m_bFoundStopper = true;
|
||||||
|
$this->m_bFoundManualOperation = true;
|
||||||
|
if (isset($aData['issue_security']))
|
||||||
|
{
|
||||||
|
$this->m_bFoundSecurityIssue = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($aData['mode'] == DEL_MANUAL)
|
||||||
|
{
|
||||||
|
$this->m_bFoundStopper = true;
|
||||||
|
$this->m_bFoundManualDelete = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach($this->m_aToUpdate as $sClass => $aToUpdate)
|
||||||
|
{
|
||||||
|
foreach($aToUpdate as $iId => $aData)
|
||||||
|
{
|
||||||
|
$this->m_iToUpdate++;
|
||||||
|
|
||||||
|
$oObject = $aData['to_reset'];
|
||||||
|
$aExtKeyLabels = array();
|
||||||
|
foreach ($aData['attributes'] as $sRemoteExtKey => $aRemoteAttDef)
|
||||||
|
{
|
||||||
|
$oObject->Set($sRemoteExtKey, 0);
|
||||||
|
$aExtKeyLabels[] = $aRemoteAttDef->GetLabel();
|
||||||
|
}
|
||||||
|
$this->m_aToUpdate[$sClass][$iId]['attributes_list'] = implode(', ', $aExtKeyLabels);
|
||||||
|
|
||||||
|
list($bRes, $aIssues, $bSecurityIssues) = $oObject->CheckToWrite();
|
||||||
|
if (!$bRes)
|
||||||
|
{
|
||||||
|
$this->m_aToUpdate[$sClass][$iId]['issue'] = implode(', ', $aIssues);
|
||||||
|
$this->m_bFoundStopper = true;
|
||||||
|
|
||||||
|
if ($bSecurityIssues)
|
||||||
|
{
|
||||||
|
$this->m_aToUpdate[$sClass][$iId]['issue_security'] = true;
|
||||||
|
$this->m_bFoundSecurityIssue = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function GetIssues()
|
||||||
|
{
|
||||||
|
$aIssues = array();
|
||||||
|
foreach ($this->m_aToDelete as $sClass => $aToDelete)
|
||||||
|
{
|
||||||
|
foreach ($aToDelete as $iId => $aData)
|
||||||
|
{
|
||||||
|
if (isset($aData['issue']))
|
||||||
|
{
|
||||||
|
$aIssues[] = $aData['issue'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
foreach ($this->m_aToUpdate as $sClass => $aToUpdate)
|
||||||
|
{
|
||||||
|
foreach ($aToUpdate as $iId => $aData)
|
||||||
|
{
|
||||||
|
if (isset($aData['issue']))
|
||||||
|
{
|
||||||
|
$aIssues[] = $aData['issue'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $aIssues;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function ListDeletes()
|
||||||
|
{
|
||||||
|
return $this->m_aToDelete;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function ListUpdates()
|
||||||
|
{
|
||||||
|
return $this->m_aToUpdate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function GetTargetCount()
|
||||||
|
{
|
||||||
|
return $this->m_iToDelete + $this->m_iToUpdate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function FoundStopper()
|
||||||
|
{
|
||||||
|
return $this->m_bFoundStopper;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function FoundSecurityIssue()
|
||||||
|
{
|
||||||
|
return $this->m_bFoundSecurityIssue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function FoundManualOperation()
|
||||||
|
{
|
||||||
|
return $this->m_bFoundManualOperation;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function FoundManualDelete()
|
||||||
|
{
|
||||||
|
return $this->m_bFoundManualDelete;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function FoundManualUpdate()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public function AddToDelete($oObject, $iDeletionMode = null)
|
||||||
|
{
|
||||||
|
if (is_null($iDeletionMode))
|
||||||
|
{
|
||||||
|
$bRequestedExplicitely = true;
|
||||||
|
$iDeletionMode = DEL_AUTO;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$bRequestedExplicitely = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$sClass = get_class($oObject);
|
||||||
|
$iId = $oObject->GetKey();
|
||||||
|
|
||||||
|
if (isset($this->m_aToUpdate[$sClass][$iId]))
|
||||||
|
{
|
||||||
|
unset($this->m_aToUpdate[$sClass][$iId]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($this->m_aToDelete[$sClass][$iId]))
|
||||||
|
{
|
||||||
|
if ($this->m_aToDelete[$sClass][$iId]['requested_explicitely'])
|
||||||
|
{
|
||||||
|
// No change: let it in mode DEL_AUTO
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$iPrevDeletionMode = $this->m_aToDelete[$sClass][$iId]['mode'];
|
||||||
|
$iNewDeletionMode = self::$m_aModeUpdate[$iPrevDeletionMode][$iDeletionMode];
|
||||||
|
$this->m_aToDelete[$sClass][$iId]['mode'] = $iNewDeletionMode;
|
||||||
|
|
||||||
|
if ($bRequestedExplicitely)
|
||||||
|
{
|
||||||
|
// This object was in the root list
|
||||||
|
$this->m_aToDelete[$sClass][$iId]['requested_explicitely'] = true;
|
||||||
|
$this->m_aToDelete[$sClass][$iId]['mode'] = DEL_AUTO;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$this->m_aToDelete[$sClass][$iId] = array(
|
||||||
|
'to_delete' => $oObject,
|
||||||
|
'mode' => $iDeletionMode,
|
||||||
|
'requested_explicitely' => $bRequestedExplicitely,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function SetDeletionIssues($oObject, $aIssues, $bSecurityIssue)
|
||||||
|
{
|
||||||
|
if (count($aIssues) > 0)
|
||||||
|
{
|
||||||
|
$sClass = get_class($oObject);
|
||||||
|
$iId = $oObject->GetKey();
|
||||||
|
$this->m_aToDelete[$sClass][$iId]['issue'] = implode(', ', $aIssues);
|
||||||
|
if ($bSecurityIssue)
|
||||||
|
{
|
||||||
|
$this->m_aToDelete[$sClass][$iId]['issue_security'] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function AddToUpdate($oObject, $oAttDef)
|
||||||
|
{
|
||||||
|
$sClass = get_class($oObject);
|
||||||
|
$iId = $oObject->GetKey();
|
||||||
|
if (isset($this->m_aToDelete[$sClass][$iId]))
|
||||||
|
{
|
||||||
|
// skip... it should be deleted anyhow !
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!isset($this->m_aToUpdate[$sClass][$iId]))
|
||||||
|
{
|
||||||
|
$this->m_aToUpdate[$sClass][$iId] = array(
|
||||||
|
'to_reset' => $oObject,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
$this->m_aToUpdate[$sClass][$iId]['attributes'][$oAttDef->GetCode()] = $oAttDef;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
||||||
@@ -163,6 +163,7 @@ class Dict
|
|||||||
{
|
{
|
||||||
$sLocalizedFormat = self::S($sFormatCode);
|
$sLocalizedFormat = self::S($sFormatCode);
|
||||||
$aArguments = func_get_args();
|
$aArguments = func_get_args();
|
||||||
|
array_shift($aArguments);
|
||||||
|
|
||||||
if ($sLocalizedFormat == $sFormatCode)
|
if ($sLocalizedFormat == $sFormatCode)
|
||||||
{
|
{
|
||||||
@@ -170,7 +171,6 @@ class Dict
|
|||||||
return $sFormatCode.' - '.implode(', ', $aArguments);
|
return $sFormatCode.' - '.implode(', ', $aArguments);
|
||||||
}
|
}
|
||||||
|
|
||||||
array_shift($aArguments);
|
|
||||||
return vsprintf($sLocalizedFormat, $aArguments);
|
return vsprintf($sLocalizedFormat, $aArguments);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -560,8 +560,7 @@ Dict::Add('EN US', 'English', 'English', array(
|
|||||||
'Core:Synchro:ThisObjectIsSynchronized' => 'This object is synchronized with an external data source',
|
'Core:Synchro:ThisObjectIsSynchronized' => 'This object is synchronized with an external data source',
|
||||||
'Core:Synchro:TheObjectWasCreatedBy_Source' => 'The object was <b>created</b> by the external data source %1$s',
|
'Core:Synchro:TheObjectWasCreatedBy_Source' => 'The object was <b>created</b> by the external data source %1$s',
|
||||||
'Core:Synchro:TheObjectCanBeDeletedBy_Source' => 'The object <b>can be deleted</b> by the external data source %1$s',
|
'Core:Synchro:TheObjectCanBeDeletedBy_Source' => 'The object <b>can be deleted</b> by the external data source %1$s',
|
||||||
|
'Core:Synchro:TheObjectCannotBeDeletedByUser_Source' => 'You <b>cannot delete the object</b> because it is owned by the external data source %1$s',
|
||||||
|
|
||||||
));
|
));
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|||||||
181
pages/UI.php
181
pages/UI.php
@@ -28,102 +28,26 @@
|
|||||||
*/
|
*/
|
||||||
function DeleteObjects(WebPage $oP, $sClass, $aObjects, $bDeleteConfirmed)
|
function DeleteObjects(WebPage $oP, $sClass, $aObjects, $bDeleteConfirmed)
|
||||||
{
|
{
|
||||||
$bFoundStopper = false;
|
$oDeletionPlan = new DeletionPlan();
|
||||||
$bFoundManuelOp = false; // A manual operation is needed
|
|
||||||
$bFoundManualDel = false; // Manual deletion (or change an ext key) is needed
|
|
||||||
$bFoundSecurityIssue = false; // Some operation not allowed to the end-user
|
|
||||||
|
|
||||||
$iTotalDelete = 0; // count of object that must be deleted
|
|
||||||
$iTotalReset = 0; // count of object for which an ext key will be reset (to 0)
|
|
||||||
$aTotalDeletedObjs = array();
|
|
||||||
$aTotalResetedObjs = array();
|
|
||||||
|
|
||||||
$aRequestedByUser = array();
|
|
||||||
foreach($aObjects as $oObj)
|
foreach($aObjects as $oObj)
|
||||||
{
|
{
|
||||||
$aRequestedByUser[] = $oObj->GetKey();
|
if ($bDeleteConfirmed)
|
||||||
}
|
|
||||||
|
|
||||||
foreach($aObjects as $oObj)
|
|
||||||
{
|
|
||||||
// Evaluate the impact on the DB integrity
|
|
||||||
//
|
|
||||||
$aDeletedObjs = array();
|
|
||||||
$aResetedObjs = array();
|
|
||||||
$oObj->GetDeletionScheme($aDeletedObjs, $aResetedObjs);
|
|
||||||
|
|
||||||
// Evaluate feasibility (user access control)
|
|
||||||
//
|
|
||||||
foreach ($aDeletedObjs as $sRemoteClass => $aDeletes)
|
|
||||||
{
|
{
|
||||||
$iTotalDelete += count($aDeletes);
|
// Prepare the change reporting
|
||||||
foreach ($aDeletes as $iId => $aData)
|
//
|
||||||
{
|
$oMyChange = MetaModel::NewObject("CMDBChange");
|
||||||
$oToDelete = $aData['to_delete'];
|
$oMyChange->Set("date", time());
|
||||||
$aTotalDeletedObjs[$sRemoteClass][$iId]['to_delete'] = $oToDelete;
|
$sUserString = CMDBChange::GetCurrentUserName();
|
||||||
if (in_array($iId, $aRequestedByUser))
|
$oMyChange->Set("userinfo", $sUserString);
|
||||||
{
|
$oMyChange->DBInsert();
|
||||||
$aTotalDeletedObjs[$sRemoteClass][$iId]['requested_by_user'] = true;
|
|
||||||
}
|
|
||||||
$bDeleteAllowed = UserRights::IsActionAllowed($sClass, UR_ACTION_DELETE, DBObjectSet::FromObject($oToDelete));
|
|
||||||
$aTotalDeletedObjs[$sRemoteClass][$iId]['auto_delete'] = $aData['auto_delete'];
|
|
||||||
if (!$bDeleteAllowed)
|
|
||||||
{
|
|
||||||
$aTotalDeletedObjs[$sRemoteClass][$iId]['issue'] = Dict::S('UI:Delete:NotAllowedToDelete');
|
|
||||||
$bFoundStopper = true;
|
|
||||||
$bFoundSecurityIssue = true;
|
|
||||||
}
|
|
||||||
elseif (isset($aData['issues']) && (count($aData['issues']) > 0))
|
|
||||||
{
|
|
||||||
$sIssueDesc = implode(', ', $aData['issues']);
|
|
||||||
$aTotalDeletedObjs[$sRemoteClass][$iId]['issue'] = $sIssueDesc;
|
|
||||||
$bFoundStopper = true;
|
|
||||||
$bFoundManuelOp = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
$bAutoDel = $aData['auto_delete'];
|
|
||||||
if (!$bAutoDel)
|
|
||||||
{
|
|
||||||
$bFoundStopper = true;
|
|
||||||
$bFoundManualDel = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($aResetedObjs as $sRemoteClass => $aToReset)
|
$oObj->DBDeleteTracked($oMyChange, null, $oDeletionPlan);
|
||||||
{
|
}
|
||||||
$iTotalReset += count($aToReset);
|
else
|
||||||
foreach ($aToReset as $iId => $aData)
|
{
|
||||||
{
|
$oObj->CheckToDelete($oDeletionPlan);
|
||||||
$oToReset = $aData['to_reset'];
|
|
||||||
$aExtKeyLabels = array();
|
|
||||||
$aForbiddenKeys = array(); // keys on which the current user is not allowed to write
|
|
||||||
foreach ($aData['attributes'] as $sRemoteExtKey => $aRemoteAttDef)
|
|
||||||
{
|
|
||||||
$bUpdateAllowed = UserRights::IsActionAllowedOnAttribute($sClass, $sRemoteExtKey, UR_ACTION_MODIFY, DBObjectSet::FromObject($oToReset));
|
|
||||||
if (!$bUpdateAllowed)
|
|
||||||
{
|
|
||||||
$bFoundStopper = true;
|
|
||||||
$bFoundSecurityIssue = true;
|
|
||||||
$aForbiddenKeys[] = $aRemoteAttDef->GetLabel();
|
|
||||||
}
|
|
||||||
$aExtKeyLabels[] = $aRemoteAttDef->GetLabel();
|
|
||||||
}
|
|
||||||
$aResetedObjs[$sRemoteClass][$iId]['attributes_list'] = implode(', ', $aExtKeyLabels);
|
|
||||||
$aTotalResetedObjs[$sRemoteClass][$iId]['attributes_list'] = $aResetedObjs[$sRemoteClass][$iId]['attributes_list'];
|
|
||||||
$aTotalResetedObjs[$sRemoteClass][$iId]['attributes'] = $aResetedObjs[$sRemoteClass][$iId]['attributes'];
|
|
||||||
if (count($aForbiddenKeys) > 0)
|
|
||||||
{
|
|
||||||
$aTotalResetedObjs[$sRemoteClass][$iId]['issue'] = Dict::Format('UI:Delete:NotAllowedToUpdate_Fields',implode(', ', $aForbiddenKeys));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$aTotalResetedObjs[$sRemoteClass][$iId]['to_reset'] = $oToReset;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// Count of dependent objects (+ the current one)
|
|
||||||
$iTotalTargets = $iTotalDelete + $iTotalReset;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($bDeleteConfirmed)
|
if ($bDeleteConfirmed)
|
||||||
@@ -138,37 +62,29 @@ function DeleteObjects(WebPage $oP, $sClass, $aObjects, $bDeleteConfirmed)
|
|||||||
$oP->add("<h1>".Dict::Format('UI:Title:BulkDeletionOf_Count_ObjectsOf_Class', count($aObjects), MetaModel::GetName($sClass))."</h1>\n");
|
$oP->add("<h1>".Dict::Format('UI:Title:BulkDeletionOf_Count_ObjectsOf_Class', count($aObjects), MetaModel::GetName($sClass))."</h1>\n");
|
||||||
}
|
}
|
||||||
// Security - do not allow the user to force a forbidden delete by the mean of page arguments...
|
// Security - do not allow the user to force a forbidden delete by the mean of page arguments...
|
||||||
if ($bFoundSecurityIssue)
|
if ($oDeletionPlan->FoundSecurityIssue())
|
||||||
{
|
{
|
||||||
throw new CoreException(Dict::S('UI:Error:NotEnoughRightsToDelete'));
|
throw new CoreException(Dict::S('UI:Error:NotEnoughRightsToDelete'));
|
||||||
}
|
}
|
||||||
if ($bFoundManuelOp)
|
if ($oDeletionPlan->FoundManualOperation())
|
||||||
{
|
{
|
||||||
throw new CoreException(Dict::S('UI:Error:CannotDeleteBecauseManualOpNeeded'));
|
throw new CoreException(Dict::S('UI:Error:CannotDeleteBecauseManualOpNeeded'));
|
||||||
}
|
}
|
||||||
if ($bFoundManualDel)
|
if ($oDeletionPlan->FoundManualDelete())
|
||||||
{
|
{
|
||||||
throw new CoreException(Dict::S('UI:Error:CannotDeleteBecauseOfDepencies'));
|
throw new CoreException(Dict::S('UI:Error:CannotDeleteBecauseOfDepencies'));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prepare the change reporting
|
// Report deletions
|
||||||
//
|
|
||||||
$oMyChange = MetaModel::NewObject("CMDBChange");
|
|
||||||
$oMyChange->Set("date", time());
|
|
||||||
$sUserString = CMDBChange::GetCurrentUserName();
|
|
||||||
$oMyChange->Set("userinfo", $sUserString);
|
|
||||||
$oMyChange->DBInsert();
|
|
||||||
|
|
||||||
// Delete dependencies
|
|
||||||
//
|
//
|
||||||
$aDisplayData = array();
|
$aDisplayData = array();
|
||||||
foreach ($aTotalDeletedObjs as $sRemoteClass => $aDeletes)
|
foreach ($oDeletionPlan->ListDeletes() as $sTargetClass => $aDeletes)
|
||||||
{
|
{
|
||||||
foreach ($aDeletes as $iId => $aData)
|
foreach ($aDeletes as $iId => $aData)
|
||||||
{
|
{
|
||||||
$oToDelete = $aData['to_delete'];
|
$oToDelete = $aData['to_delete'];
|
||||||
|
|
||||||
if (isset($aData['requested_by_user']))
|
if (isset($aData['requested_explicitely']))
|
||||||
{
|
{
|
||||||
$sMessage = Dict::S('UI:Delete:Deleted');
|
$sMessage = Dict::S('UI:Delete:Deleted');
|
||||||
}
|
}
|
||||||
@@ -181,35 +97,27 @@ function DeleteObjects(WebPage $oP, $sClass, $aObjects, $bDeleteConfirmed)
|
|||||||
'object' => $oToDelete->GetHyperLink(),
|
'object' => $oToDelete->GetHyperLink(),
|
||||||
'consequence' => $sMessage,
|
'consequence' => $sMessage,
|
||||||
);
|
);
|
||||||
|
|
||||||
$oToDelete->DBDeleteTracked($oMyChange);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update dependencies
|
// Report updates
|
||||||
//
|
//
|
||||||
foreach ($aTotalResetedObjs as $sRemoteClass => $aToReset)
|
foreach ($oDeletionPlan->ListUpdates() as $sTargetClass => $aToUpdate)
|
||||||
{
|
{
|
||||||
foreach ($aToReset as $iId => $aData)
|
foreach ($aToUpdate as $iId => $aData)
|
||||||
{
|
{
|
||||||
$oToReset = $aData['to_reset'];
|
$oToUpdate = $aData['to_reset'];
|
||||||
$aDisplayData[] = array(
|
$aDisplayData[] = array(
|
||||||
'class' => MetaModel::GetName(get_class($oToReset)),
|
'class' => MetaModel::GetName(get_class($oToUpdate)),
|
||||||
'object' => $oToReset->GetHyperLink(),
|
'object' => $oToUpdate->GetHyperLink(),
|
||||||
'consequence' => Dict::Format('UI:Delete:AutomaticResetOf_Fields', $aData['attributes_list']),
|
'consequence' => Dict::Format('UI:Delete:AutomaticResetOf_Fields', $aData['attributes_list']),
|
||||||
);
|
);
|
||||||
|
|
||||||
foreach ($aData['attributes'] as $sRemoteExtKey => $aRemoteAttDef)
|
|
||||||
{
|
|
||||||
$oToReset->Set($sRemoteExtKey, 0);
|
|
||||||
$oToReset->DBUpdateTracked($oMyChange);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Report automatic jobs
|
// Report automatic jobs
|
||||||
//
|
//
|
||||||
if ($iTotalTargets > 0)
|
if ($oDeletionPlan->GetTargetCount() > 0)
|
||||||
{
|
{
|
||||||
if (count($aObjects) == 1)
|
if (count($aObjects) == 1)
|
||||||
{
|
{
|
||||||
@@ -241,17 +149,17 @@ function DeleteObjects(WebPage $oP, $sClass, $aObjects, $bDeleteConfirmed)
|
|||||||
// Explain what should be done
|
// Explain what should be done
|
||||||
//
|
//
|
||||||
$aDisplayData = array();
|
$aDisplayData = array();
|
||||||
foreach ($aTotalDeletedObjs as $sRemoteClass => $aDeletes)
|
foreach ($oDeletionPlan->ListDeletes() as $sTargetClass => $aDeletes)
|
||||||
{
|
{
|
||||||
foreach ($aDeletes as $iId => $aData)
|
foreach ($aDeletes as $iId => $aData)
|
||||||
{
|
{
|
||||||
$oToDelete = $aData['to_delete'];
|
$oToDelete = $aData['to_delete'];
|
||||||
$bAutoDel = $aData['auto_delete'];
|
$bAutoDel = (($aData['mode'] == DEL_SILENT) || ($aData['mode'] == DEL_AUTO));
|
||||||
if (array_key_exists('issue', $aData))
|
if (array_key_exists('issue', $aData))
|
||||||
{
|
{
|
||||||
if ($bAutoDel)
|
if ($bAutoDel)
|
||||||
{
|
{
|
||||||
if (isset($aData['requested_by_user']))
|
if (isset($aData['requested_explicitely']))
|
||||||
{
|
{
|
||||||
$sConsequence = Dict::Format('UI:Delete:CannotDeleteBecause', $aData['issue']);
|
$sConsequence = Dict::Format('UI:Delete:CannotDeleteBecause', $aData['issue']);
|
||||||
}
|
}
|
||||||
@@ -269,7 +177,7 @@ function DeleteObjects(WebPage $oP, $sClass, $aObjects, $bDeleteConfirmed)
|
|||||||
{
|
{
|
||||||
if ($bAutoDel)
|
if ($bAutoDel)
|
||||||
{
|
{
|
||||||
if (isset($aData['requested_by_user']))
|
if (isset($aData['requested_explicitely']))
|
||||||
{
|
{
|
||||||
$sConsequence = ''; // not applicable
|
$sConsequence = ''; // not applicable
|
||||||
}
|
}
|
||||||
@@ -290,11 +198,11 @@ function DeleteObjects(WebPage $oP, $sClass, $aObjects, $bDeleteConfirmed)
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
foreach ($aTotalResetedObjs as $sRemoteClass => $aToReset)
|
foreach ($oDeletionPlan->ListUpdates() as $sRemoteClass => $aToUpdate)
|
||||||
{
|
{
|
||||||
foreach ($aToReset as $iId => $aData)
|
foreach ($aToUpdate as $iId => $aData)
|
||||||
{
|
{
|
||||||
$oToReset = $aData['to_reset'];
|
$oToUpdate = $aData['to_reset'];
|
||||||
if (array_key_exists('issue', $aData))
|
if (array_key_exists('issue', $aData))
|
||||||
{
|
{
|
||||||
$sConsequence = Dict::Format('UI:Delete:CannotUpdateBecause_Issue', $aData['issue']);
|
$sConsequence = Dict::Format('UI:Delete:CannotUpdateBecause_Issue', $aData['issue']);
|
||||||
@@ -304,29 +212,29 @@ function DeleteObjects(WebPage $oP, $sClass, $aObjects, $bDeleteConfirmed)
|
|||||||
$sConsequence = Dict::Format('UI:Delete:WillAutomaticallyUpdate_Fields', $aData['attributes_list']);
|
$sConsequence = Dict::Format('UI:Delete:WillAutomaticallyUpdate_Fields', $aData['attributes_list']);
|
||||||
}
|
}
|
||||||
$aDisplayData[] = array(
|
$aDisplayData[] = array(
|
||||||
'class' => MetaModel::GetName(get_class($oToReset)),
|
'class' => MetaModel::GetName(get_class($oToUpdate)),
|
||||||
'object' => $oToReset->GetHyperLink(),
|
'object' => $oToUpdate->GetHyperLink(),
|
||||||
'consequence' => $sConsequence,
|
'consequence' => $sConsequence,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$iInducedDeletions = $iTotalTargets - count($aObjects);
|
$iImpactedIndirectly = $oDeletionPlan->GetTargetCount() - count($aObjects);
|
||||||
if ($iInducedDeletions > 0)
|
if ($iImpactedIndirectly > 0)
|
||||||
{
|
{
|
||||||
if (count($aObjects) == 1)
|
if (count($aObjects) == 1)
|
||||||
{
|
{
|
||||||
$oObj = $aObjects[0];
|
$oObj = $aObjects[0];
|
||||||
$oP->p(Dict::Format('UI:Delete:Count_Objects/LinksReferencing_Object', $iInducedDeletions, $oObj->GetName()));
|
$oP->p(Dict::Format('UI:Delete:Count_Objects/LinksReferencing_Object', $iImpactedIndirectly, $oObj->GetName()));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
$oP->p(Dict::Format('UI:Delete:Count_Objects/LinksReferencingTheObjects', $iInducedDeletions));
|
$oP->p(Dict::Format('UI:Delete:Count_Objects/LinksReferencingTheObjects', $iImpactedIndirectly));
|
||||||
}
|
}
|
||||||
$oP->p(Dict::S('UI:Delete:ReferencesMustBeDeletedToEnsureIntegrity'));
|
$oP->p(Dict::S('UI:Delete:ReferencesMustBeDeletedToEnsureIntegrity'));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (($iInducedDeletions > 0) || $bFoundStopper)
|
if (($iImpactedIndirectly > 0) || $oDeletionPlan->FoundStopper())
|
||||||
{
|
{
|
||||||
$aDisplayConfig = array();
|
$aDisplayConfig = array();
|
||||||
$aDisplayConfig['class'] = array('label' => 'Class', 'description' => '');
|
$aDisplayConfig['class'] = array('label' => 'Class', 'description' => '');
|
||||||
@@ -335,13 +243,13 @@ function DeleteObjects(WebPage $oP, $sClass, $aObjects, $bDeleteConfirmed)
|
|||||||
$oP->table($aDisplayConfig, $aDisplayData);
|
$oP->table($aDisplayConfig, $aDisplayData);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($bFoundStopper)
|
if ($oDeletionPlan->FoundStopper())
|
||||||
{
|
{
|
||||||
if ($bFoundSecurityIssue)
|
if ($oDeletionPlan->FoundSecurityIssue())
|
||||||
{
|
{
|
||||||
$oP->p(Dict::S('UI:Delete:SorryDeletionNotAllowed'));
|
$oP->p(Dict::S('UI:Delete:SorryDeletionNotAllowed'));
|
||||||
}
|
}
|
||||||
elseif ($bFoundManualDel)
|
elseif ($oDeletionPlan->FoundManualOperation())
|
||||||
{
|
{
|
||||||
$oP->p(Dict::S('UI:Delete:PleaseDoTheManualOperations'));
|
$oP->p(Dict::S('UI:Delete:PleaseDoTheManualOperations'));
|
||||||
}
|
}
|
||||||
@@ -390,7 +298,6 @@ function DeleteObjects(WebPage $oP, $sClass, $aObjects, $bDeleteConfirmed)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -76,20 +76,33 @@ class SynchroDataSource extends cmdbAbstractObject
|
|||||||
|
|
||||||
MetaModel::Init_AddAttribute(new AttributeLinkedSet("attribute_list", array("linked_class"=>"SynchroAttribute", "ext_key_to_me"=>"sync_source_id", "allowed_values"=>null, "count_min"=>0, "count_max"=>0, "depends_on"=>array())));
|
MetaModel::Init_AddAttribute(new AttributeLinkedSet("attribute_list", array("linked_class"=>"SynchroAttribute", "ext_key_to_me"=>"sync_source_id", "allowed_values"=>null, "count_min"=>0, "count_max"=>0, "depends_on"=>array())));
|
||||||
// Not used yet !
|
// Not used yet !
|
||||||
MetaModel::Init_AddAttribute(new AttributeEnum("user_delete_policy", array("allowed_values"=>new ValueSetEnum('never,depends,always'), "sql"=>"user_delete_policy", "default_value"=>"always", "is_null_allowed"=>true, "depends_on"=>array())));
|
MetaModel::Init_AddAttribute(new AttributeEnum("user_delete_policy", array("allowed_values"=>new ValueSetEnum('everybody,administrators,nobody'), "sql"=>"user_delete_policy", "default_value"=>"nobody", "is_null_allowed"=>true, "depends_on"=>array())));
|
||||||
|
|
||||||
MetaModel::Init_AddAttribute(new AttributeURL("url_icon", array("allowed_values"=>null, "sql"=>"url_icon", "default_value"=>null, "is_null_allowed"=>true, "target"=> '_top', "depends_on"=>array())));
|
MetaModel::Init_AddAttribute(new AttributeURL("url_icon", array("allowed_values"=>null, "sql"=>"url_icon", "default_value"=>null, "is_null_allowed"=>true, "target"=> '_top', "depends_on"=>array())));
|
||||||
// The field below is not a real URL since it can contain placeholders like $replica->primary_key$ which are not syntactically allowed in a real URL
|
// The field below is not a real URL since it can contain placeholders like $replica->primary_key$ which are not syntactically allowed in a real URL
|
||||||
MetaModel::Init_AddAttribute(new AttributeString("url_application", array("allowed_values"=>null, "sql"=>"url_application", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
|
MetaModel::Init_AddAttribute(new AttributeString("url_application", array("allowed_values"=>null, "sql"=>"url_application", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
|
||||||
|
|
||||||
// Display lists
|
// Display lists
|
||||||
MetaModel::Init_SetZListItems('details', array('name', 'description', 'url_icon', 'url_application', 'scope_class', /*'scope_restriction', */'status', 'user_id', 'full_load_periodicity', 'reconciliation_policy', 'action_on_zero', 'action_on_one', 'action_on_multiple', 'delete_policy', 'delete_policy_update', 'delete_policy_retention' /*'attribute_list'*/)); // Attributes to be displayed for the complete details
|
MetaModel::Init_SetZListItems('details', array('name', 'description', 'url_icon', 'url_application', 'scope_class', /*'scope_restriction', */'status', 'user_id', 'full_load_periodicity', 'reconciliation_policy', 'action_on_zero', 'action_on_one', 'action_on_multiple', 'delete_policy', 'delete_policy_update', 'delete_policy_retention', 'user_delete_policy')); // Attributes to be displayed for the complete details
|
||||||
MetaModel::Init_SetZListItems('list', array('scope_class', 'status', 'user_id', 'full_load_periodicity')); // Attributes to be displayed for a list
|
MetaModel::Init_SetZListItems('list', array('scope_class', 'status', 'user_id', 'full_load_periodicity')); // Attributes to be displayed for a list
|
||||||
// Search criteria
|
// Search criteria
|
||||||
MetaModel::Init_SetZListItems('standard_search', array('name', 'status', 'scope_class', 'user_id')); // Criteria of the std search form
|
MetaModel::Init_SetZListItems('standard_search', array('name', 'status', 'scope_class', 'user_id')); // Criteria of the std search form
|
||||||
// MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form
|
// MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static $m_oCurrentTask = null;
|
||||||
|
public static function GetCurrentTaskId()
|
||||||
|
{
|
||||||
|
if (is_object(self::$m_oCurrentTask))
|
||||||
|
{
|
||||||
|
return self::$m_oCurrentTask->GetKey();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public function DisplayBareRelations(WebPage $oPage, $bEditMode = false)
|
public function DisplayBareRelations(WebPage $oPage, $bEditMode = false)
|
||||||
{
|
{
|
||||||
if (!$this->IsNew())
|
if (!$this->IsNew())
|
||||||
@@ -406,13 +419,13 @@ EOF
|
|||||||
return str_replace($aSearches, $aReplacements, $this->Get('url_application'));
|
return str_replace($aSearches, $aReplacements, $this->Get('url_application'));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function GetAttributeFlags($sAttCode)
|
public function GetAttributeFlags($sAttCode, &$aReasons = array())
|
||||||
{
|
{
|
||||||
if (($sAttCode == 'scope_class') && (!$this->IsNew()))
|
if (($sAttCode == 'scope_class') && (!$this->IsNew()))
|
||||||
{
|
{
|
||||||
return OPT_ATT_READONLY;
|
return OPT_ATT_READONLY;
|
||||||
}
|
}
|
||||||
return parent::GetAttributeFlags($sAttCode);
|
return parent::GetAttributeFlags($sAttCode, $aReasons);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function UpdateObject($sFormPrefix = '')
|
public function UpdateObject($sFormPrefix = '')
|
||||||
@@ -690,6 +703,7 @@ EOF
|
|||||||
|
|
||||||
$oStatLog->DBInsertTracked($oMyChange);
|
$oStatLog->DBInsertTracked($oMyChange);
|
||||||
|
|
||||||
|
self::$m_oCurrentTask = $this;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
$this->DoSynchronize($oLastFullLoadStartDate, $oMyChange, $oStatLog);
|
$this->DoSynchronize($oLastFullLoadStartDate, $oMyChange, $oStatLog);
|
||||||
@@ -713,6 +727,7 @@ EOF
|
|||||||
$oStatLog->Set('last_error', $e->getMessage());
|
$oStatLog->Set('last_error', $e->getMessage());
|
||||||
$oStatLog->DBUpdateTracked($oMyChange);
|
$oStatLog->DBUpdateTracked($oMyChange);
|
||||||
}
|
}
|
||||||
|
self::$m_oCurrentTask = null;
|
||||||
return $oStatLog;
|
return $oStatLog;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -998,7 +1013,7 @@ class SynchroAttribute extends cmdbAbstractObject
|
|||||||
);
|
);
|
||||||
MetaModel::Init_Params($aParams);
|
MetaModel::Init_Params($aParams);
|
||||||
MetaModel::Init_InheritAttributes();
|
MetaModel::Init_InheritAttributes();
|
||||||
MetaModel::Init_AddAttribute(new AttributeExternalKey("sync_source_id", array("targetclass"=>"SynchroDataSource", "jointype"=> "", "allowed_values"=>null, "sql"=>"sync_source_id", "is_null_allowed"=>false, "on_target_delete"=>DEL_AUTO, "depends_on"=>array())));
|
MetaModel::Init_AddAttribute(new AttributeExternalKey("sync_source_id", array("targetclass"=>"SynchroDataSource", "jointype"=> "", "allowed_values"=>null, "sql"=>"sync_source_id", "is_null_allowed"=>false, "on_target_delete"=>DEL_SILENT, "depends_on"=>array())));
|
||||||
MetaModel::Init_AddAttribute(new AttributeString("attcode", array("allowed_values"=>null, "sql"=>"attcode", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
|
MetaModel::Init_AddAttribute(new AttributeString("attcode", array("allowed_values"=>null, "sql"=>"attcode", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
|
||||||
MetaModel::Init_AddAttribute(new AttributeBoolean("update", array("allowed_values"=>null, "sql"=>"update", "default_value"=>true, "is_null_allowed"=>false, "depends_on"=>array())));
|
MetaModel::Init_AddAttribute(new AttributeBoolean("update", array("allowed_values"=>null, "sql"=>"update", "default_value"=>true, "is_null_allowed"=>false, "depends_on"=>array())));
|
||||||
MetaModel::Init_AddAttribute(new AttributeBoolean("reconcile", array("allowed_values"=>null, "sql"=>"reconcile", "default_value"=>false, "is_null_allowed"=>false, "depends_on"=>array())));
|
MetaModel::Init_AddAttribute(new AttributeBoolean("reconcile", array("allowed_values"=>null, "sql"=>"reconcile", "default_value"=>false, "is_null_allowed"=>false, "depends_on"=>array())));
|
||||||
@@ -1113,7 +1128,7 @@ class SynchroLog extends DBObject
|
|||||||
MetaModel::Init_Params($aParams);
|
MetaModel::Init_Params($aParams);
|
||||||
MetaModel::Init_InheritAttributes();
|
MetaModel::Init_InheritAttributes();
|
||||||
// MetaModel::Init_AddAttribute(new AttributeString("userinfo", array("allowed_values"=>null, "sql"=>"userinfo", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
|
// MetaModel::Init_AddAttribute(new AttributeString("userinfo", array("allowed_values"=>null, "sql"=>"userinfo", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
|
||||||
MetaModel::Init_AddAttribute(new AttributeExternalKey("sync_source_id", array("targetclass"=>"SynchroDataSource", "jointype"=> "", "allowed_values"=>null, "sql"=>"sync_source_id", "is_null_allowed"=>false, "on_target_delete"=>DEL_AUTO, "depends_on"=>array())));
|
MetaModel::Init_AddAttribute(new AttributeExternalKey("sync_source_id", array("targetclass"=>"SynchroDataSource", "jointype"=> "", "allowed_values"=>null, "sql"=>"sync_source_id", "is_null_allowed"=>false, "on_target_delete"=>DEL_SILENT, "depends_on"=>array())));
|
||||||
MetaModel::Init_AddAttribute(new AttributeDateTime("start_date", array("allowed_values"=>null, "sql"=>"start_date", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array())));
|
MetaModel::Init_AddAttribute(new AttributeDateTime("start_date", array("allowed_values"=>null, "sql"=>"start_date", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array())));
|
||||||
MetaModel::Init_AddAttribute(new AttributeDateTime("end_date", array("allowed_values"=>null, "sql"=>"end_date", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array())));
|
MetaModel::Init_AddAttribute(new AttributeDateTime("end_date", array("allowed_values"=>null, "sql"=>"end_date", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array())));
|
||||||
MetaModel::Init_AddAttribute(new AttributeEnum("status", array("allowed_values"=>new ValueSetEnum('running,completed,error'), "sql"=>"status", "default_value"=>"running", "is_null_allowed"=>false, "depends_on"=>array())));
|
MetaModel::Init_AddAttribute(new AttributeEnum("status", array("allowed_values"=>new ValueSetEnum('running,completed,error'), "sql"=>"status", "default_value"=>"running", "is_null_allowed"=>false, "depends_on"=>array())));
|
||||||
@@ -1244,7 +1259,7 @@ class SynchroReplica extends DBObject implements iDisplay
|
|||||||
MetaModel::Init_Params($aParams);
|
MetaModel::Init_Params($aParams);
|
||||||
MetaModel::Init_InheritAttributes();
|
MetaModel::Init_InheritAttributes();
|
||||||
|
|
||||||
MetaModel::Init_AddAttribute(new AttributeExternalKey("sync_source_id", array("targetclass"=>"SynchroDataSource", "jointype"=> "", "allowed_values"=>null, "sql"=>"sync_source_id", "is_null_allowed"=>false, "on_target_delete"=>DEL_AUTO, "depends_on"=>array())));
|
MetaModel::Init_AddAttribute(new AttributeExternalKey("sync_source_id", array("targetclass"=>"SynchroDataSource", "jointype"=> "", "allowed_values"=>null, "sql"=>"sync_source_id", "is_null_allowed"=>false, "on_target_delete"=>DEL_SILENT, "depends_on"=>array())));
|
||||||
MetaModel::Init_AddAttribute(new AttributeExternalField("base_class", array("allowed_values"=>null, "extkey_attcode"=> 'sync_source_id', "target_attcode"=>"scope_class")));
|
MetaModel::Init_AddAttribute(new AttributeExternalField("base_class", array("allowed_values"=>null, "extkey_attcode"=> 'sync_source_id', "target_attcode"=>"scope_class")));
|
||||||
|
|
||||||
MetaModel::Init_AddAttribute(new AttributeInteger("dest_id", array("allowed_values"=>null, "sql"=>"dest_id", "default_value"=>0, "is_null_allowed"=>true, "depends_on"=>array())));
|
MetaModel::Init_AddAttribute(new AttributeInteger("dest_id", array("allowed_values"=>null, "sql"=>"dest_id", "default_value"=>0, "is_null_allowed"=>true, "depends_on"=>array())));
|
||||||
@@ -1273,7 +1288,7 @@ class SynchroReplica extends DBObject implements iDisplay
|
|||||||
|
|
||||||
// Overload the deletion -> the replica has been created by the mean of a trigger,
|
// Overload the deletion -> the replica has been created by the mean of a trigger,
|
||||||
// it will be deleted by the mean of a trigger too
|
// it will be deleted by the mean of a trigger too
|
||||||
public function DBDelete()
|
public function DBDelete(&$oDeletionPlan = null)
|
||||||
{
|
{
|
||||||
$oDataSource = MetaModel::GetObject('SynchroDataSource', $this->Get('sync_source_id'));
|
$oDataSource = MetaModel::GetObject('SynchroDataSource', $this->Get('sync_source_id'));
|
||||||
$sTable = $oDataSource->GetDataTable();
|
$sTable = $oDataSource->GetDataTable();
|
||||||
|
|||||||
Reference in New Issue
Block a user