#439 Record and display changes in the link sets (ex: Members of a team)

#439 Make sure that changes made by a plugin get recorded
+ simplified the change tracking for the plugins. Simply call DBObject::DBInsert (resp. Update and Delete) and the change will be recorded for the current page. This is compatible with the old (not mandatory anymore) way that was requiring DBInsertTracked APIs (resp. Update, Delete).

SVN:trunk[2236]
This commit is contained in:
Romain Quetiez
2012-10-08 12:17:56 +00:00
parent 60cf926fdb
commit 7dbbb1c299
24 changed files with 622 additions and 294 deletions

View File

@@ -1424,6 +1424,8 @@ abstract class DBObject
$oTrigger->DoActivate($this->ToArgs('this'));
}
$this->RecordObjCreation();
return $this->m_iKey;
}
@@ -1434,13 +1436,15 @@ abstract class DBObject
return $this->m_iKey;
}
public function DBInsertTracked(CMDBChange $oVoid)
public function DBInsertTracked(CMDBChange $oChange)
{
CMDBObject::SetCurrentChange($oChange);
return $this->DBInsert();
}
public function DBInsertTrackedNoReload(CMDBChange $oVoid)
public function DBInsertTrackedNoReload(CMDBChange $oChange)
{
CMDBObject::SetCurrentChange($oChange);
return $this->DBInsertNoReload();
}
@@ -1450,7 +1454,9 @@ abstract class DBObject
{
$this->m_bIsInDB = false;
$this->m_iKey = $iNewKey;
return $this->DBInsert();
$ret = $this->DBInsert();
$this->RecordObjCreation();
return $ret;
}
/**
@@ -1498,7 +1504,7 @@ abstract class DBObject
$aChanges = $this->ListChanges();
if (count($aChanges) == 0)
{
//throw new CoreWarning("Attempting to update an unchanged object");
// Attempting to update an unchanged object
return;
}
@@ -1510,6 +1516,9 @@ abstract class DBObject
throw new CoreException("Object not following integrity rules", array('issues' => $sIssues, 'class' => get_class($this), 'id' => $this->GetKey()));
}
// Save the original values (will be reset to the new values when the object get written to the DB)
$aOriginalValues = $this->m_aOrigValues;
$bHasANewExternalKeyValue = false;
$aHierarchicalKeys = array();
foreach($aChanges as $sAttCode => $valuecurr)
@@ -1588,11 +1597,17 @@ abstract class DBObject
$this->Reload();
}
if (count($aChanges) != 0)
{
$this->RecordAttChanges($aChanges, $aOriginalValues);
}
return $this->m_iKey;
}
public function DBUpdateTracked(CMDBChange $oVoid)
public function DBUpdateTracked(CMDBChange $oChange)
{
CMDBObject::SetCurrentChange($oChange);
return $this->DBUpdate();
}
@@ -1664,6 +1679,8 @@ abstract class DBObject
$this->AfterDelete();
$this->RecordObjDeletion($this->m_iKey);
$this->m_bIsInDB = false;
$this->m_iKey = null;
}
@@ -1719,8 +1736,9 @@ abstract class DBObject
return $oDeletionPlan;
}
public function DBDeleteTracked(CMDBChange $oVoid, $bSkipStrongSecurity = null, &$oDeletionPlan = null)
public function DBDeleteTracked(CMDBChange $oChange, $bSkipStrongSecurity = null, &$oDeletionPlan = null)
{
CMDBObject::SetCurrentChange($oChange);
$this->DBDelete($oDeletionPlan);
}
@@ -1899,6 +1917,152 @@ abstract class DBObject
{
}
/**
* Common to the recording of link set changes (add/remove/modify)
*/
private function PrepareChangeOpLinkSet($iLinkSetOwnerId, $oLinkSet, $sChangeOpClass, $aOriginalValues = null)
{
if ($iLinkSetOwnerId <= 0)
{
return null;
}
if (!is_subclass_of($oLinkSet->GetHostClass(), 'CMDBObject'))
{
// The link set owner class does not keep track of its history
return null;
}
// Determine the linked item class and id
//
if ($oLinkSet->IsIndirect())
{
// The "item" is on the other end (N-N links)
$sExtKeyToRemote = $oLinkSet->GetExtKeyToRemote();
$oExtKeyToRemote = MetaModel::GetAttributeDef(get_class($this), $sExtKeyToRemote);
$sItemClass = $oExtKeyToRemote->GetTargetClass();
if ($aOriginalValues)
{
// Get the value from the original values
$iItemId = $aOriginalValues[$sExtKeyToRemote];
}
else
{
$iItemId = $this->Get($sExtKeyToRemote);
}
}
else
{
// I am the "item" (1-N links)
$sItemClass = get_class($this);
$iItemId = $this->GetKey();
}
// Get the remote object, to determine its exact class
// Possible optimization: implement a tool in MetaModel, to get the final class of an object (not always querying + query reduced to a select on the root table!
$oOwner = MetaModel::GetObject($oLinkSet->GetHostClass(), $iLinkSetOwnerId, false);
if ($oOwner)
{
$sLinkSetOwnerClass = get_class($oOwner);
$oMyChangeOp = MetaModel::NewObject($sChangeOpClass);
$oMyChangeOp->Set("objclass", $sLinkSetOwnerClass);
$oMyChangeOp->Set("objkey", $iLinkSetOwnerId);
$oMyChangeOp->Set("attcode", $oLinkSet->GetCode());
$oMyChangeOp->Set("item_class", $sItemClass);
$oMyChangeOp->Set("item_id", $iItemId);
return $oMyChangeOp;
}
else
{
// Depending on the deletion order, it may happen that the id is already invalid... ignore
return null;
}
}
/**
* This object has been created/deleted, record that as a change in link sets pointing to this (if any)
*/
private function RecordLinkSetListChange($bAdd = true)
{
$aForwardChangeTracking = MetaModel::GetTrackForwardExternalKeys(get_class($this));
foreach(MetaModel::GetTrackForwardExternalKeys(get_class($this)) as $sExtKeyAttCode => $oLinkSet)
{
if (($oLinkSet->GetTrackingLevel() & LINKSET_TRACKING_LIST) == 0) continue;
$iLinkSetOwnerId = $this->Get($sExtKeyAttCode);
$oMyChangeOp = $this->PrepareChangeOpLinkSet($iLinkSetOwnerId, $oLinkSet, 'CMDBChangeOpSetAttributeLinksAddRemove');
if ($oMyChangeOp)
{
if ($bAdd)
{
$oMyChangeOp->Set("type", "added");
}
else
{
$oMyChangeOp->Set("type", "removed");
}
$iId = $oMyChangeOp->DBInsertNoReload();
}
}
}
protected function RecordObjCreation()
{
$this->RecordLinkSetListChange(true);
}
protected function RecordObjDeletion($objkey)
{
$this->RecordLinkSetListChange(false);
}
protected function RecordAttChanges(array $aValues, array $aOrigValues)
{
$aForwardChangeTracking = MetaModel::GetTrackForwardExternalKeys(get_class($this));
foreach(MetaModel::GetTrackForwardExternalKeys(get_class($this)) as $sExtKeyAttCode => $oLinkSet)
{
if (array_key_exists($sExtKeyAttCode, $aValues))
{
if (($oLinkSet->GetTrackingLevel() & LINKSET_TRACKING_LIST) == 0) continue;
// Keep track of link added/removed
//
$iLinkSetOwnerNext = $aValues[$sExtKeyAttCode];
$oMyChangeOp = $this->PrepareChangeOpLinkSet($iLinkSetOwnerNext, $oLinkSet, 'CMDBChangeOpSetAttributeLinksAddRemove');
if ($oMyChangeOp)
{
$oMyChangeOp->Set("type", "added");
$oMyChangeOp->DBInsertNoReload();
}
$iLinkSetOwnerPrevious = $aOrigValues[$sExtKeyAttCode];
$oMyChangeOp = $this->PrepareChangeOpLinkSet($iLinkSetOwnerPrevious, $oLinkSet, 'CMDBChangeOpSetAttributeLinksAddRemove', $aOrigValues);
if ($oMyChangeOp)
{
$oMyChangeOp->Set("type", "removed");
$oMyChangeOp->DBInsertNoReload();
}
}
else
{
// Keep track of link changes
//
if (($oLinkSet->GetTrackingLevel() & LINKSET_TRACKING_DETAILS) == 0) continue;
$iLinkSetOwnerId = $this->Get($sExtKeyAttCode);
$oMyChangeOp = $this->PrepareChangeOpLinkSet($iLinkSetOwnerId, $oLinkSet, 'CMDBChangeOpSetAttributeLinksTune');
if ($oMyChangeOp)
{
$oMyChangeOp->Set("link_id", $this->GetKey());
$iId = $oMyChangeOp->DBInsertNoReload();
}
}
}
}
// Return an empty set for the parent of all
public static function GetRelationQueries($sRelCode)
{
@@ -2103,7 +2267,7 @@ abstract class DBObject
}
// to be continued...
}
}
?>