mirror of
https://github.com/Combodo/iTop.git
synced 2026-02-13 07:24:13 +01:00
Integration of the new way to compute relations into the datamodel (ComputeImpactedItems)
SVN:trunk[3570]
This commit is contained in:
@@ -2546,7 +2546,7 @@ abstract class DBObject implements iDisplay
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Will be deprecated soon - use MetaModel::GetRelatedObjectsDown/Up instead to take redundancy into account
|
* Will be deprecated soon - use GetRelatedObjectsDown/Up instead to take redundancy into account
|
||||||
*/
|
*/
|
||||||
public function GetRelatedObjects($sRelCode, $iMaxDepth = 99, &$aResults = array())
|
public function GetRelatedObjects($sRelCode, $iMaxDepth = 99, &$aResults = array())
|
||||||
{
|
{
|
||||||
@@ -2608,7 +2608,43 @@ abstract class DBObject implements iDisplay
|
|||||||
}
|
}
|
||||||
return $aResults;
|
return $aResults;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compute the "RelatedObjects" (forward or "down" direction) for the object
|
||||||
|
* for the specified relation
|
||||||
|
*
|
||||||
|
* @param string $sRelCode The code of the relation to use for the computation
|
||||||
|
* @param int $iMaxDepth Maximum recursion depth
|
||||||
|
* @param boolean $bEnableReduncancy Whether or not to take into account the redundancy
|
||||||
|
*
|
||||||
|
* @return RelationGraph The graph of all the related objects
|
||||||
|
*/
|
||||||
|
public function GetRelatedObjectsDown($sRelCode, $iMaxDepth = 99, $bEnableRedundancy = true)
|
||||||
|
{
|
||||||
|
$oGraph = new RelationGraph();
|
||||||
|
$oGraph->AddSourceObject($this);
|
||||||
|
$oGraph->ComputeRelatedObjectsDown($sRelCode, $iMaxDepth, $bEnableRedundancy);
|
||||||
|
return $oGraph;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compute the "RelatedObjects" (reverse or "up" direction) for the object
|
||||||
|
* for the specified relation
|
||||||
|
*
|
||||||
|
* @param string $sRelCode The code of the relation to use for the computation
|
||||||
|
* @param int $iMaxDepth Maximum recursion depth
|
||||||
|
* @param boolean $bEnableReduncancy Whether or not to take into account the redundancy
|
||||||
|
*
|
||||||
|
* @return RelationGraph The graph of all the related objects
|
||||||
|
*/
|
||||||
|
public function GetRelatedObjectsUp($sRelCode, $iMaxDepth = 99, $bEnableRedundancy = true)
|
||||||
|
{
|
||||||
|
$oGraph = new RelationGraph();
|
||||||
|
$oGraph->AddSourceObject($this);
|
||||||
|
$oGraph->ComputeRelatedObjectsUp($sRelCode, $iMaxDepth, $bEnableRedundancy);
|
||||||
|
return $oGraph;
|
||||||
|
}
|
||||||
|
|
||||||
public function GetReferencingObjects($bAllowAllData = false)
|
public function GetReferencingObjects($bAllowAllData = false)
|
||||||
{
|
{
|
||||||
$aDependentObjects = array();
|
$aDependentObjects = array();
|
||||||
|
|||||||
@@ -992,6 +992,50 @@ class DBObjectSet
|
|||||||
return $aRelatedObjs;
|
return $aRelatedObjs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compute the "RelatedObjects" (forward or "down" direction) for the set
|
||||||
|
* for the specified relation
|
||||||
|
*
|
||||||
|
* @param string $sRelCode The code of the relation to use for the computation
|
||||||
|
* @param int $iMaxDepth Maximum recursion depth
|
||||||
|
* @param boolean $bEnableReduncancy Whether or not to take into account the redundancy
|
||||||
|
*
|
||||||
|
* @return RelationGraph The graph of all the related objects
|
||||||
|
*/
|
||||||
|
public function GetRelatedObjectsDown($sRelCode, $iMaxDepth = 99, $bEnableRedundancy = true)
|
||||||
|
{
|
||||||
|
$oGraph = new RelationGraph();
|
||||||
|
$this->Rewind();
|
||||||
|
while($oObj = $this->Fetch())
|
||||||
|
{
|
||||||
|
$oGraph->AddSourceObject($oObj);
|
||||||
|
}
|
||||||
|
$oGraph->ComputeRelatedObjectsDown($sRelCode, $iMaxDepth, $bEnableRedundancy);
|
||||||
|
return $oGraph;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compute the "RelatedObjects" (reverse or "up" direction) for the set
|
||||||
|
* for the specified relation
|
||||||
|
*
|
||||||
|
* @param string $sRelCode The code of the relation to use for the computation
|
||||||
|
* @param int $iMaxDepth Maximum recursion depth
|
||||||
|
* @param boolean $bEnableReduncancy Whether or not to take into account the redundancy
|
||||||
|
*
|
||||||
|
* @return RelationGraph The graph of all the related objects
|
||||||
|
*/
|
||||||
|
public function GetRelatedObjectsUp($sRelCode, $iMaxDepth = 99, $bEnableRedundancy = true)
|
||||||
|
{
|
||||||
|
$oGraph = new RelationGraph();
|
||||||
|
$this->Rewind();
|
||||||
|
while($oObj = $this->Fetch())
|
||||||
|
{
|
||||||
|
$oGraph->AddSinkObject($oObj);
|
||||||
|
}
|
||||||
|
$oGraph->ComputeRelatedObjectsUp($sRelCode, $iMaxDepth, $bEnableRedundancy);
|
||||||
|
return $oGraph;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Builds an object that contains the values that are common to all the objects
|
* Builds an object that contains the values that are common to all the objects
|
||||||
* in the set. If for a given attribute, objects in the set have various values
|
* in the set. If for a given attribute, objects in the set have various values
|
||||||
|
|||||||
@@ -88,6 +88,8 @@ class DisplayableNode extends GraphNode
|
|||||||
$aNode['icon_url'] = $this->GetIconURL();
|
$aNode['icon_url'] = $this->GetIconURL();
|
||||||
$aNode['width'] = 32;
|
$aNode['width'] = 32;
|
||||||
$aNode['source'] = ($this->GetProperty('source') == true);
|
$aNode['source'] = ($this->GetProperty('source') == true);
|
||||||
|
$aNode['obj_class'] = get_class($this->GetProperty('object'));
|
||||||
|
$aNode['obj_key'] = $this->GetProperty('object')->GetKey();
|
||||||
$aNode['sink'] = ($this->GetProperty('sink') == true);
|
$aNode['sink'] = ($this->GetProperty('sink') == true);
|
||||||
$aNode['x'] = $this->x;
|
$aNode['x'] = $this->x;
|
||||||
$aNode['y']= $this->y;
|
$aNode['y']= $this->y;
|
||||||
@@ -291,6 +293,7 @@ class DisplayableNode extends GraphNode
|
|||||||
if ($oGraph->GetNode($oNode->GetId()))
|
if ($oGraph->GetNode($oNode->GetId()))
|
||||||
{
|
{
|
||||||
$oGraph->_RemoveNode($oNode);
|
$oGraph->_RemoveNode($oNode);
|
||||||
|
$oNewNode->AddObject($oNode->GetProperty('object'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$oNewNode->GroupSimilarNeighbours($oGraph, $iThresholdCount, $bDirectionUp, $bDirectionDown);
|
$oNewNode->GroupSimilarNeighbours($oGraph, $iThresholdCount, $bDirectionUp, $bDirectionDown);
|
||||||
@@ -412,6 +415,7 @@ class DisplayableRedundancyNode extends DisplayableNode
|
|||||||
}
|
}
|
||||||
//echo "<p>Replacing ".$oNode->GetId().' by '.$oNewNode->GetId()."\n";
|
//echo "<p>Replacing ".$oNode->GetId().' by '.$oNewNode->GetId()."\n";
|
||||||
$oGraph->_RemoveNode($oNode);
|
$oGraph->_RemoveNode($oNode);
|
||||||
|
$oNewNode->AddObject($oNode->GetProperty('object'));
|
||||||
}
|
}
|
||||||
//$oNewNode->GroupSimilarNeighbours($oGraph, $iThresholdCount, $bDirectionUp, $bDirectionDown);
|
//$oNewNode->GroupSimilarNeighbours($oGraph, $iThresholdCount, $bDirectionUp, $bDirectionDown);
|
||||||
}
|
}
|
||||||
@@ -471,6 +475,24 @@ class DisplayableEdge extends GraphEdge
|
|||||||
|
|
||||||
class DisplayableGroupNode extends DisplayableNode
|
class DisplayableGroupNode extends DisplayableNode
|
||||||
{
|
{
|
||||||
|
protected $aObjects;
|
||||||
|
|
||||||
|
public function __construct(SimpleGraph $oGraph, $sId, $x = 0, $y = 0)
|
||||||
|
{
|
||||||
|
parent::__construct($oGraph, $sId, $x, $y);
|
||||||
|
$this->aObjects = array();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function AddObject(DBObject $oObj)
|
||||||
|
{
|
||||||
|
$this->aObjects[$oObj->GetKey()] = $oObj;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function GetObjects()
|
||||||
|
{
|
||||||
|
return $this->aObjects;
|
||||||
|
}
|
||||||
|
|
||||||
public function GetWidth()
|
public function GetWidth()
|
||||||
{
|
{
|
||||||
return 50;
|
return 50;
|
||||||
@@ -487,6 +509,7 @@ class DisplayableGroupNode extends DisplayableNode
|
|||||||
$aNode['y']= $this->y;
|
$aNode['y']= $this->y;
|
||||||
$aNode['label'] = $this->GetLabel();
|
$aNode['label'] = $this->GetLabel();
|
||||||
$aNode['id'] = $this->GetId();
|
$aNode['id'] = $this->GetId();
|
||||||
|
$aNode['group_index'] = $this->GetProperty('group_index'); // if supplied
|
||||||
$fDiscOpacity = ($this->GetProperty('is_reached') ? 1 : 0.2);
|
$fDiscOpacity = ($this->GetProperty('is_reached') ? 1 : 0.2);
|
||||||
$fTextOpacity = ($this->GetProperty('is_reached') ? 1 : 0.4);
|
$fTextOpacity = ($this->GetProperty('is_reached') ? 1 : 0.4);
|
||||||
$aNode['icon_attr'] = array('opacity' => $fTextOpacity);
|
$aNode['icon_attr'] = array('opacity' => $fTextOpacity);
|
||||||
@@ -583,6 +606,7 @@ class DisplayableGraph extends SimpleGraph
|
|||||||
}
|
}
|
||||||
$oObj = $oNode->GetProperty('object');
|
$oObj = $oNode->GetProperty('object');
|
||||||
$oNewNode->SetProperty('class', get_class($oObj));
|
$oNewNode->SetProperty('class', get_class($oObj));
|
||||||
|
$oNewNode->SetProperty('object', $oObj);
|
||||||
$oNewNode->SetProperty('icon_url', $oObj->GetIcon(false));
|
$oNewNode->SetProperty('icon_url', $oObj->GetIcon(false));
|
||||||
$oNewNode->SetProperty('label', $oObj->GetRawName());
|
$oNewNode->SetProperty('label', $oObj->GetRawName());
|
||||||
$oNewNode->SetProperty('is_reached', $bDirectionDown ? $oNode->GetProperty('is_reached') : true); // When going "up" is_reached does not matter
|
$oNewNode->SetProperty('is_reached', $bDirectionDown ? $oNode->GetProperty('is_reached') : true); // When going "up" is_reached does not matter
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ require_once(APPROOT.'core/querybuildercontext.class.inc.php');
|
|||||||
require_once(APPROOT.'core/querymodifier.class.inc.php');
|
require_once(APPROOT.'core/querymodifier.class.inc.php');
|
||||||
require_once(APPROOT.'core/metamodelmodifier.inc.php');
|
require_once(APPROOT.'core/metamodelmodifier.inc.php');
|
||||||
require_once(APPROOT.'core/computing.inc.php');
|
require_once(APPROOT.'core/computing.inc.php');
|
||||||
|
require_once(APPROOT.'core/relationgraph.class.inc.php');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Metamodel
|
* Metamodel
|
||||||
|
|||||||
@@ -417,30 +417,108 @@ class CoreServices implements iRestServiceProvider
|
|||||||
$key = RestUtils::GetMandatoryParam($aParams, 'key');
|
$key = RestUtils::GetMandatoryParam($aParams, 'key');
|
||||||
$sRelation = RestUtils::GetMandatoryParam($aParams, 'relation');
|
$sRelation = RestUtils::GetMandatoryParam($aParams, 'relation');
|
||||||
$iMaxRecursionDepth = RestUtils::GetOptionalParam($aParams, 'depth', 20 /* = MAX_RECURSION_DEPTH */);
|
$iMaxRecursionDepth = RestUtils::GetOptionalParam($aParams, 'depth', 20 /* = MAX_RECURSION_DEPTH */);
|
||||||
|
$sDirection = RestUtils::GetOptionalParam($aParams, 'direction', null);
|
||||||
|
$bEnableRedundancy = RestUtils::GetOptionalParam($aParams, 'redundancy', false);
|
||||||
|
$bReverse = false;
|
||||||
|
|
||||||
|
if (is_null($sDirection) && ($sRelation == 'depends on'))
|
||||||
|
{
|
||||||
|
// Legacy behavior, consider "depends on" as a forward relation
|
||||||
|
$sRelation = 'impacts';
|
||||||
|
$sDirection = 'up';
|
||||||
|
$bReverse = true; // emulate the legacy behavior by returning the edges
|
||||||
|
}
|
||||||
|
else if(is_null($sDirection))
|
||||||
|
{
|
||||||
|
$sDirection = 'down';
|
||||||
|
}
|
||||||
|
|
||||||
$oObjectSet = RestUtils::GetObjectSetFromKey($sClass, $key);
|
$oObjectSet = RestUtils::GetObjectSetFromKey($sClass, $key);
|
||||||
$aIndexByClass = array();
|
if ($sDirection == 'down')
|
||||||
while ($oObject = $oObjectSet->Fetch())
|
|
||||||
{
|
{
|
||||||
$aRelated = array();
|
$oRelationGraph = $oObjectSet->GetRelatedObjectsDown($sRelation, $iMaxRecursionDepth, $bEnableRedundancy);
|
||||||
$aGraph = array();
|
}
|
||||||
$aIndexByClass[get_class($oObject)][$oObject->GetKey()] = null;
|
else if ($sDirection == 'up')
|
||||||
$oResult->AddObject(0, '', $oObject);
|
{
|
||||||
$this->GetRelatedObjects($oObject, $sRelation, $iMaxRecursionDepth, $aRelated, $aGraph);
|
$oRelationGraph = $oObjectSet->GetRelatedObjectsUp($sRelation, $iMaxRecursionDepth, $bEnableRedundancy);
|
||||||
|
}
|
||||||
foreach($aRelated as $sClass => $aObjects)
|
else
|
||||||
|
{
|
||||||
|
$oResult->code = RestResult::INTERNAL_ERROR;
|
||||||
|
$oResult->message = "Invalid value: '$sDirection' for the parameter 'direction'. Valid values are 'up' and 'down'";
|
||||||
|
return $oResult;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($bEnableRedundancy)
|
||||||
|
{
|
||||||
|
// Remove the redundancy nodes from the output
|
||||||
|
$oIterator = new RelationTypeIterator($oRelationGraph, 'Node');
|
||||||
|
foreach($oIterator as $oNode)
|
||||||
{
|
{
|
||||||
foreach($aObjects as $oRelatedObj)
|
if ($oNode instanceof RelationRedundancyNode)
|
||||||
{
|
{
|
||||||
$aIndexByClass[get_class($oRelatedObj)][$oRelatedObj->GetKey()] = null;
|
$oRelationGraph->FilterNode($oNode);
|
||||||
$oResult->AddObject(0, '', $oRelatedObj);
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
foreach($aGraph as $sSrcKey => $aDestinations)
|
}
|
||||||
|
|
||||||
|
$aIndexByClass = array();
|
||||||
|
$oIterator = new RelationTypeIterator($oRelationGraph);
|
||||||
|
foreach($oIterator as $oElement)
|
||||||
|
{
|
||||||
|
if ($oElement instanceof RelationObjectNode)
|
||||||
{
|
{
|
||||||
foreach ($aDestinations as $sDestKey)
|
$oObject = $oElement->GetProperty('object');
|
||||||
|
if ($oObject)
|
||||||
{
|
{
|
||||||
$oResult->AddRelation($sSrcKey, $sDestKey);
|
if ($bEnableRedundancy)
|
||||||
|
{
|
||||||
|
// Add only the "reached" objects
|
||||||
|
if ($oElement->GetProperty('is_reached'))
|
||||||
|
{
|
||||||
|
$aIndexByClass[get_class($oObject)][$oObject->GetKey()] = null;
|
||||||
|
$oResult->AddObject(0, '', $oObject);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$aIndexByClass[get_class($oObject)][$oObject->GetKey()] = null;
|
||||||
|
$oResult->AddObject(0, '', $oObject);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if ($oElement instanceof RelationEdge)
|
||||||
|
{
|
||||||
|
$oSrcObj = $oElement->GetSourceNode()->GetProperty('object');
|
||||||
|
$oDestObj = $oElement->GetSinkNode()->GetProperty('object');
|
||||||
|
$sSrcKey = get_class($oSrcObj).'::'.$oSrcObj->GetKey();
|
||||||
|
$sDestKey = get_class($oDestObj).'::'.$oDestObj->GetKey();
|
||||||
|
if ($bEnableRedundancy)
|
||||||
|
{
|
||||||
|
// Add only the edges where both source and destination are "reached"
|
||||||
|
if ($oElement->GetSourceNode()->GetProperty('is_reached') && $oElement->GetSinkNode()->GetProperty('is_reached'))
|
||||||
|
{
|
||||||
|
if ($bReverse)
|
||||||
|
{
|
||||||
|
$oResult->AddRelation($sDestKey, $sSrcKey);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$oResult->AddRelation($sSrcKey, $sDestKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if ($bReverse)
|
||||||
|
{
|
||||||
|
$oResult->AddRelation($sDestKey, $sSrcKey);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$oResult->AddRelation($sSrcKey, $sDestKey);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -606,38 +684,4 @@ class CoreServices implements iRestServiceProvider
|
|||||||
$oResult->message = $sRes;
|
$oResult->message = $sRes;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Helper function to get the related objects up to the given depth along with the "graph" of the relation
|
|
||||||
* @param DBObject $oObject Starting point of the computation
|
|
||||||
* @param string $sRelation Code of the relation (i.e; 'impact', 'depends on'...)
|
|
||||||
* @param integer $iMaxRecursionDepth Maximum level of recursion
|
|
||||||
* @param Hash $aRelated Two dimensions hash of the already related objects: array( 'class' => array(key => ))
|
|
||||||
* @param Hash $aGraph Hash array for the topology of the relation: source => related: array('class:key' => array( DBObjects ))
|
|
||||||
* @param integer $iRecursionDepth Current level of recursion
|
|
||||||
*/
|
|
||||||
protected function GetRelatedObjects(DBObject $oObject, $sRelation, $iMaxRecursionDepth, &$aRelated, &$aGraph, $iRecursionDepth = 1)
|
|
||||||
{
|
|
||||||
// Avoid loops
|
|
||||||
if ((array_key_exists(get_class($oObject), $aRelated)) && (array_key_exists($oObject->GetKey(), $aRelated[get_class($oObject)]))) return;
|
|
||||||
// Stop at maximum recursion level
|
|
||||||
if ($iRecursionDepth > $iMaxRecursionDepth) return;
|
|
||||||
|
|
||||||
$sSrcKey = get_class($oObject).'::'.$oObject->GetKey();
|
|
||||||
$aNewRelated = array();
|
|
||||||
$oObject->GetRelatedObjects($sRelation, 1, $aNewRelated);
|
|
||||||
foreach($aNewRelated as $sClass => $aObjects)
|
|
||||||
{
|
|
||||||
if (!array_key_exists($sSrcKey, $aGraph))
|
|
||||||
{
|
|
||||||
$aGraph[$sSrcKey] = array();
|
|
||||||
}
|
|
||||||
foreach($aObjects as $oRelatedObject)
|
|
||||||
{
|
|
||||||
$aRelated[$sClass][$oRelatedObject->GetKey()] = $oRelatedObject;
|
|
||||||
$aGraph[$sSrcKey][] = get_class($oRelatedObject).'::'.$oRelatedObject->GetKey();
|
|
||||||
$this->GetRelatedObjects($oRelatedObject, $sRelation, $iMaxRecursionDepth, $aRelated, $aGraph, $iRecursionDepth+1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -337,6 +337,41 @@ class SimpleGraph
|
|||||||
unset($this->aNodes[$oNode->GetId()]);
|
unset($this->aNodes[$oNode->GetId()]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes the given node but preserves the connectivity of the graph
|
||||||
|
* all "source" nodes are connected to all "sink" nodes
|
||||||
|
* @param GraphNode $oNode
|
||||||
|
* @throws SimpleGraphException
|
||||||
|
*/
|
||||||
|
public function FilterNode(GraphNode $oNode)
|
||||||
|
{
|
||||||
|
if (!array_key_exists($oNode->GetId(), $this->aNodes)) throw new SimpleGraphException('Cannot filter the node (id='.$oNode->GetId().') from the graph. The node was not found in the graph.');
|
||||||
|
|
||||||
|
$aSourceNodes = array();
|
||||||
|
$aSinkNodes = array();
|
||||||
|
foreach($oNode->GetOutgoingEdges() as $oEdge)
|
||||||
|
{
|
||||||
|
$sSinkId = $oEdge->GetSinkNode()->GetId();
|
||||||
|
$aSinkNodes[$sSinkId] = $oEdge->GetSinkNode();
|
||||||
|
$this->_RemoveEdge($oEdge);
|
||||||
|
}
|
||||||
|
foreach($oNode->GetIncomingEdges() as $oEdge)
|
||||||
|
{
|
||||||
|
$sSourceId = $oEdge->GetSourceNode()->GetId();
|
||||||
|
$aSourceNodes[$sSourceId] = $oEdge->GetSourceNode();
|
||||||
|
$this->_RemoveEdge($oEdge);
|
||||||
|
}
|
||||||
|
unset($this->aNodes[$oNode->GetId()]);
|
||||||
|
|
||||||
|
foreach($aSourceNodes as $sSourceId => $oSourceNode)
|
||||||
|
{
|
||||||
|
foreach($aSinkNodes as $sSinkId => $oSinkNode)
|
||||||
|
{
|
||||||
|
$oEdge = new RelationEdge($this, $oSourceNode, $oSinkNode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the node identified by $sId or null if not found
|
* Get the node identified by $sId or null if not found
|
||||||
* @param string $sId
|
* @param string $sId
|
||||||
@@ -499,9 +534,10 @@ EOF
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the description of the graph as an embedded PNG image (using a data: url) as
|
* Get the description of the graph as text string in the XDot format
|
||||||
* generated by graphviz (requires graphviz to be installed on the machine and the path to
|
* including the positions of the nodes and egdes (requires graphviz
|
||||||
* dot/dot.exe to be configured in the iTop configuration file)
|
* to be installed on the machine and the path to dot/dot.exe to be
|
||||||
|
* configured in the iTop configuration file)
|
||||||
* Note: the function creates temporary files in APPROOT/data/tmp
|
* Note: the function creates temporary files in APPROOT/data/tmp
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1368,7 +1368,21 @@
|
|||||||
|
|
||||||
$oImpactedInfras = DBObjectSet::FromLinkSet($this, 'functionalcis_list', 'functionalci_id');
|
$oImpactedInfras = DBObjectSet::FromLinkSet($this, 'functionalcis_list', 'functionalci_id');
|
||||||
|
|
||||||
$aComputed = $oImpactedInfras->GetRelatedObjects('impacts', 10);
|
$oGraph = $oImpactedInfras->GetRelatedObjectsDown('impacts',10, true /* bEnableRedundancy */);
|
||||||
|
$oIterator = new RelationTypeIterator($oGraph, 'Node');
|
||||||
|
foreach($oIterator as $oNode)
|
||||||
|
{
|
||||||
|
if($oNode instanceof RelationObjectNode)
|
||||||
|
{
|
||||||
|
$oObj = $oNode->GetProperty('object');
|
||||||
|
$sRootClass = MetaModel::GetRootClass(get_class($oObj));
|
||||||
|
if (!array_key_exists($sRootClass, $aComputed))
|
||||||
|
{
|
||||||
|
$aComputed[$sRootClass] = array();
|
||||||
|
}
|
||||||
|
$aComputed[$sRootClass][$oObj->GetKey()] = $oObj;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (isset($aComputed['FunctionalCI']) && is_array($aComputed['FunctionalCI']))
|
if (isset($aComputed['FunctionalCI']) && is_array($aComputed['FunctionalCI']))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1370,8 +1370,25 @@
|
|||||||
|
|
||||||
$oImpactedInfras = DBObjectSet::FromLinkSet($this, 'functionalcis_list', 'functionalci_id');
|
$oImpactedInfras = DBObjectSet::FromLinkSet($this, 'functionalcis_list', 'functionalci_id');
|
||||||
|
|
||||||
$aComputed = $oImpactedInfras->GetRelatedObjects('impacts', 10);
|
$oGraph = $oImpactedInfras->GetRelatedObjectsDown('impacts',10, true /* bEnableRedundancy */);
|
||||||
|
$oIterator = new RelationTypeIterator($oGraph, 'Node');
|
||||||
|
foreach($oIterator as $oNode)
|
||||||
|
{
|
||||||
|
if($oNode instanceof RelationObjectNode)
|
||||||
|
{
|
||||||
|
if ($oNode->GetProperty('is_reached'))
|
||||||
|
{
|
||||||
|
$oObj = $oNode->GetProperty('object');
|
||||||
|
$sRootClass = MetaModel::GetRootClass(get_class($oObj));
|
||||||
|
if (!array_key_exists($sRootClass, $aComputed))
|
||||||
|
{
|
||||||
|
$aComputed[$sRootClass] = array();
|
||||||
|
}
|
||||||
|
$aComputed[$sRootClass][$oObj->GetKey()] = $oObj;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (isset($aComputed['FunctionalCI']) && is_array($aComputed['FunctionalCI']))
|
if (isset($aComputed['FunctionalCI']) && is_array($aComputed['FunctionalCI']))
|
||||||
{
|
{
|
||||||
foreach($aComputed['FunctionalCI'] as $iKey => $oObject)
|
foreach($aComputed['FunctionalCI'] as $iKey => $oObject)
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ $(function()
|
|||||||
.addClass('graph');
|
.addClass('graph');
|
||||||
|
|
||||||
this._create_toolkit_menu();
|
this._create_toolkit_menu();
|
||||||
|
this._build_context_menus();
|
||||||
},
|
},
|
||||||
|
|
||||||
// called when created, and later when changing options
|
// called when created, and later when changing options
|
||||||
@@ -134,13 +135,13 @@ $(function()
|
|||||||
oNode.aElements.push(this.oPaper.image(oNode.icon_url, xPos - iWidth * this.fZoom/2, yPos - iHeight * this.fZoom/2, iWidth*this.fZoom, iHeight*this.fZoom).attr(oNode.icon_attr));
|
oNode.aElements.push(this.oPaper.image(oNode.icon_url, xPos - iWidth * this.fZoom/2, yPos - iHeight * this.fZoom/2, iWidth*this.fZoom, iHeight*this.fZoom).attr(oNode.icon_attr));
|
||||||
var oText = this.oPaper.text( xPos, yPos, oNode.label);
|
var oText = this.oPaper.text( xPos, yPos, oNode.label);
|
||||||
oText.attr(oNode.text_attr);
|
oText.attr(oNode.text_attr);
|
||||||
oText.transform('s'+this.fZoom);
|
oText.transform('S'+this.fZoom);
|
||||||
var oBB = oText.getBBox();
|
var oBB = oText.getBBox();
|
||||||
var dy = iHeight/2*this.fZoom + oBB.height/2;
|
var dy = iHeight/2*this.fZoom + oBB.height/2;
|
||||||
oText.remove();
|
oText.remove();
|
||||||
oText = this.oPaper.text( xPos, yPos + dy, oNode.label);
|
oText = this.oPaper.text( xPos, yPos + dy, oNode.label);
|
||||||
oText.attr(oNode.text_attr);
|
oText.attr(oNode.text_attr);
|
||||||
oText.transform('s'+this.fZoom);
|
oText.transform('S'+this.fZoom);
|
||||||
oNode.aElements.push(oText);
|
oNode.aElements.push(oText);
|
||||||
oNode.aElements.push(this.oPaper.rect( xPos - oBB.width/2 -2, yPos - oBB.height/2 + dy, oBB.width +4, oBB.height).attr({fill: '#fff', stroke: '#fff', opacity: 0.9}).toBack());
|
oNode.aElements.push(this.oPaper.rect( xPos - oBB.width/2 -2, yPos - oBB.height/2 + dy, oBB.width +4, oBB.height).attr({fill: '#fff', stroke: '#fff', opacity: 0.9}).toBack());
|
||||||
break;
|
break;
|
||||||
@@ -158,6 +159,7 @@ $(function()
|
|||||||
for(k in oNode.aElements)
|
for(k in oNode.aElements)
|
||||||
{
|
{
|
||||||
var sNodeId = oNode.id;
|
var sNodeId = oNode.id;
|
||||||
|
$(oNode.aElements[k].node).attr({'data-type': oNode.shape, 'data-id': oNode.id} ).attr('class', 'popupMenuTarget');
|
||||||
oNode.aElements[k].drag(function(dx, dy, x, y, event) { me._move(sNodeId, dx, dy, x, y, event); }, function(x, y, event) { me._drag_start(sNodeId, x, y, event); }, function (event) { me._drag_end(sNodeId, event); });
|
oNode.aElements[k].drag(function(dx, dy, x, y, event) { me._move(sNodeId, dx, dy, x, y, event); }, function(x, y, event) { me._drag_start(sNodeId, x, y, event); }, function (event) { me._drag_end(sNodeId, event); });
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -309,6 +311,16 @@ $(function()
|
|||||||
oEdge.aElements = [];
|
oEdge.aElements = [];
|
||||||
this.aEdges.push(oEdge);
|
this.aEdges.push(oEdge);
|
||||||
},
|
},
|
||||||
|
show_group: function(sGroupId)
|
||||||
|
{
|
||||||
|
// Activate the 3rd tab
|
||||||
|
this.element.closest('.ui-tabs').tabs("option", "active", 2);
|
||||||
|
// Scroll into view the group
|
||||||
|
if ($('#'+sGroupId).length > 0)
|
||||||
|
{
|
||||||
|
$('#'+sGroupId)[0].scrollIntoView();
|
||||||
|
}
|
||||||
|
},
|
||||||
_create_toolkit_menu: function()
|
_create_toolkit_menu: function()
|
||||||
{
|
{
|
||||||
var sPopupMenuId = 'tk_graph'+this.element.attr('id');
|
var sPopupMenuId = 'tk_graph'+this.element.attr('id');
|
||||||
@@ -333,6 +345,63 @@ $(function()
|
|||||||
$('#'+sPopupMenuId+'_document').click(function() { me.export_as_document(); });
|
$('#'+sPopupMenuId+'_document').click(function() { me.export_as_document(); });
|
||||||
$('#'+sPopupMenuId+'_reload').click(function() { me.reload(); });
|
$('#'+sPopupMenuId+'_reload').click(function() { me.reload(); });
|
||||||
|
|
||||||
|
},
|
||||||
|
_build_context_menus: function()
|
||||||
|
{
|
||||||
|
var sId = this.element.attr('id');
|
||||||
|
var me = this;
|
||||||
|
|
||||||
|
$.contextMenu({
|
||||||
|
selector: '#'+sId+' .popupMenuTarget',
|
||||||
|
build: function(trigger, e) {
|
||||||
|
// this callback is executed every time the menu is to be shown
|
||||||
|
// its results are destroyed every time the menu is hidden
|
||||||
|
// e is the original contextmenu event, containing e.pageX and e.pageY (amongst other data)
|
||||||
|
var sType = trigger.attr('data-type');
|
||||||
|
var sNodeId = trigger.attr('data-id');
|
||||||
|
var oNode = me._find_node(sNodeId);
|
||||||
|
|
||||||
|
/*
|
||||||
|
var sObjName = trigger.attr('data-class');
|
||||||
|
var sIndex = trigger.attr('data-index');
|
||||||
|
var originalEvent = e;
|
||||||
|
var bHasItems = false;
|
||||||
|
*/
|
||||||
|
var oResult = {callback: null, items: {}};
|
||||||
|
switch(sType)
|
||||||
|
{
|
||||||
|
case 'group':
|
||||||
|
var sGroupIndex = oNode.group_index;
|
||||||
|
oResult = {
|
||||||
|
callback: function(key, options) {
|
||||||
|
var me = $('.itop-simple-graph').data('itopSimple_graph'); // need a live value
|
||||||
|
me.show_group('relation_group_'+sGroupIndex);
|
||||||
|
},
|
||||||
|
items: { 'show': {name: 'Show group' } }
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'icon':
|
||||||
|
var sObjClass = oNode.obj_class;
|
||||||
|
var sObjKey = oNode.obj_key;
|
||||||
|
oResult = {
|
||||||
|
callback: function(key, options) {
|
||||||
|
var me = $('.itop-simple-graph').data('itopSimple_graph'); // need a live value
|
||||||
|
var sURL = me.options.drill_down_url.replace('%1$s', sObjClass).replace('%2$s', sObjKey);
|
||||||
|
window.location.href = sURL;
|
||||||
|
},
|
||||||
|
items: { 'details': {name: 'Show Details' } }
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
oResult = false; // No context menu
|
||||||
|
|
||||||
|
}
|
||||||
|
return oResult;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
},
|
},
|
||||||
export_as_pdf: function()
|
export_as_pdf: function()
|
||||||
{
|
{
|
||||||
|
|||||||
60
pages/UI.php
60
pages/UI.php
@@ -224,11 +224,11 @@ function DisplayNavigatorListTab($oP, $aResults, $sRelation, $oObj)
|
|||||||
{
|
{
|
||||||
$oP->SetCurrentTab(Dict::S('UI:RelationshipList'));
|
$oP->SetCurrentTab(Dict::S('UI:RelationshipList'));
|
||||||
$oP->add("<div id=\"impacted_objects\" style=\"width:100%;background-color:#fff;padding:10px;\">");
|
$oP->add("<div id=\"impacted_objects\" style=\"width:100%;background-color:#fff;padding:10px;\">");
|
||||||
|
$oP->add("<h1>".MetaModel::GetRelationDescription($sRelation).' '.$oObj->GetName()."</h1>\n");
|
||||||
$iBlock = 1; // Zero is not a valid blockid
|
$iBlock = 1; // Zero is not a valid blockid
|
||||||
foreach($aResults as $sListClass => $aObjects)
|
foreach($aResults as $sListClass => $aObjects)
|
||||||
{
|
{
|
||||||
$oSet = CMDBObjectSet::FromArray($sListClass, $aObjects);
|
$oSet = CMDBObjectSet::FromArray($sListClass, $aObjects);
|
||||||
$oP->add("<h1>".MetaModel::GetRelationDescription($sRelation).' '.$oObj->GetName()."</h1>\n");
|
|
||||||
$oP->add("<div class=\"page_header\">\n");
|
$oP->add("<div class=\"page_header\">\n");
|
||||||
$oP->add("<h2>".MetaModel::GetClassIcon($sListClass)." <span class=\"hilite\">".Dict::Format('UI:Search:Count_ObjectsOf_Class_Found', count($aObjects), Metamodel::GetName($sListClass))."</h2>\n");
|
$oP->add("<h2>".MetaModel::GetClassIcon($sListClass)." <span class=\"hilite\">".Dict::Format('UI:Search:Count_ObjectsOf_Class_Found', count($aObjects), Metamodel::GetName($sListClass))."</h2>\n");
|
||||||
$oP->add("</div>\n");
|
$oP->add("</div>\n");
|
||||||
@@ -239,7 +239,30 @@ function DisplayNavigatorListTab($oP, $aResults, $sRelation, $oObj)
|
|||||||
$oP->add("</div>");
|
$oP->add("</div>");
|
||||||
}
|
}
|
||||||
|
|
||||||
function DisplayNavigatorGraphicsTab($oP, $aResults, $oRelGraph, $sClass, $id, $sRelation, $oAppContext, $bDirectionDown)
|
function DisplayNavigatorGroupTab($oP, $aGroups, $sRelation, $oObj)
|
||||||
|
{
|
||||||
|
if (count($aGroups) > 0)
|
||||||
|
{
|
||||||
|
$oP->SetCurrentTab(Dict::S('UI:RelationGroups'));
|
||||||
|
$oP->add("<div id=\"impacted_groupss\" style=\"width:100%;background-color:#fff;padding:10px;\">");
|
||||||
|
$iBlock = 1; // Zero is not a valid blockid
|
||||||
|
foreach($aGroups as $idx => $aObjects)
|
||||||
|
{
|
||||||
|
$sListClass = get_class(current($aObjects));
|
||||||
|
$oSet = CMDBObjectSet::FromArray($sListClass, $aObjects);
|
||||||
|
$oP->add("<h1>".Dict::Format('UI:RelationGroupNumber_N', (1+$idx))."</h1>\n");
|
||||||
|
$oP->add("<div id=\"relation_group_$idx\" class=\"page_header\">\n");
|
||||||
|
$oP->add("<h2>".MetaModel::GetClassIcon($sListClass)." <span class=\"hilite\">".Dict::Format('UI:Search:Count_ObjectsOf_Class_Found', count($aObjects), Metamodel::GetName($sListClass))."</h2>\n");
|
||||||
|
$oP->add("</div>\n");
|
||||||
|
$oBlock = DisplayBlock::FromObjectSet($oSet, 'list');
|
||||||
|
$oBlock->Display($oP, 'group_'.$iBlock++);
|
||||||
|
$oP->p(' '); // Some space ?
|
||||||
|
}
|
||||||
|
$oP->add("</div>");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function DisplayNavigatorGraphicsTab($oP, $aResults, $oDisplayGraph, $sClass, $id, $sRelation, $oAppContext, $bDirectionDown)
|
||||||
{
|
{
|
||||||
$oP->SetCurrentTab(Dict::S('UI:RelationshipGraph'));
|
$oP->SetCurrentTab(Dict::S('UI:RelationshipGraph'));
|
||||||
|
|
||||||
@@ -279,11 +302,12 @@ EOF
|
|||||||
$iGroupingThreshold = utils::ReadParam('g', 5);
|
$iGroupingThreshold = utils::ReadParam('g', 5);
|
||||||
|
|
||||||
$oP->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/fraphael.js');
|
$oP->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/fraphael.js');
|
||||||
|
$oP->add_linked_stylesheet(utils::GetAbsoluteUrlAppRoot().'css/jquery.contextMenu.css');
|
||||||
|
$oP->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/jquery.contextMenu.js');
|
||||||
$oP->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/simple_graph.js');
|
$oP->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/simple_graph.js');
|
||||||
$oGraph = DisplayableGraph::FromRelationGraph($oRelGraph, $iGroupingThreshold, $bDirectionDown);
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
$oGraph->InitFromGraphviz();
|
$oDisplayGraph->InitFromGraphviz();
|
||||||
$sExportAsPdfURL = '';
|
$sExportAsPdfURL = '';
|
||||||
if (extension_loaded('gd'))
|
if (extension_loaded('gd'))
|
||||||
{
|
{
|
||||||
@@ -294,7 +318,7 @@ EOF
|
|||||||
$sDrillDownURL = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?operation=details&class=%1$s&id=%2$s&'.$sContext;
|
$sDrillDownURL = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?operation=details&class=%1$s&id=%2$s&'.$sContext;
|
||||||
$sExportAsDocumentURL = '';
|
$sExportAsDocumentURL = '';
|
||||||
|
|
||||||
$oGraph->RenderAsRaphael($oP, null, $sExportAsPdfURL, $sExportAsDocumentURL, $sDrillDownURL);
|
$oDisplayGraph->RenderAsRaphael($oP, null, $sExportAsPdfURL, $sExportAsDocumentURL, $sDrillDownURL);
|
||||||
}
|
}
|
||||||
catch(Exception $e)
|
catch(Exception $e)
|
||||||
{
|
{
|
||||||
@@ -1531,7 +1555,8 @@ EOF
|
|||||||
$id = utils::ReadParam('id', 0);
|
$id = utils::ReadParam('id', 0);
|
||||||
$sRelation = utils::ReadParam('relation', 'impact');
|
$sRelation = utils::ReadParam('relation', 'impact');
|
||||||
$sDirection = utils::ReadParam('direction', 'down');
|
$sDirection = utils::ReadParam('direction', 'down');
|
||||||
|
$iGroupingThreshold = utils::ReadParam('g', 5);
|
||||||
|
|
||||||
$oObj = MetaModel::GetObject($sClass, $id);
|
$oObj = MetaModel::GetObject($sClass, $id);
|
||||||
$iMaxRecursionDepth = MetaModel::GetConfig()->Get('relations_max_depth', 20);
|
$iMaxRecursionDepth = MetaModel::GetConfig()->Get('relations_max_depth', 20);
|
||||||
$aSourceObjects = array($oObj);
|
$aSourceObjects = array($oObj);
|
||||||
@@ -1551,10 +1576,12 @@ EOF
|
|||||||
|
|
||||||
|
|
||||||
$aResults = array();
|
$aResults = array();
|
||||||
|
$aGroups = array();
|
||||||
|
$iGroupIdx = 0;
|
||||||
$oIterator = new RelationTypeIterator($oRelGraph, 'Node');
|
$oIterator = new RelationTypeIterator($oRelGraph, 'Node');
|
||||||
foreach($oIterator as $oNode)
|
foreach($oIterator as $oNode)
|
||||||
{
|
{
|
||||||
$oObj = $oNode->GetProperty('object'); // Some nodes (Redundancy Nodes) do not contain an object
|
$oObj = $oNode->GetProperty('object'); // Some nodes (Redundancy Nodes and Group) do not contain an object
|
||||||
if ($oObj)
|
if ($oObj)
|
||||||
{
|
{
|
||||||
$sObjClass = get_class($oObj);
|
$sObjClass = get_class($oObj);
|
||||||
@@ -1565,6 +1592,19 @@ EOF
|
|||||||
$aResults[$sObjClass][] = $oObj;
|
$aResults[$sObjClass][] = $oObj;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$oDisplayGraph = DisplayableGraph::FromRelationGraph($oRelGraph, $iGroupingThreshold, ($sDirection == 'down'));
|
||||||
|
|
||||||
|
$oIterator = new RelationTypeIterator($oDisplayGraph, 'Node');
|
||||||
|
foreach($oIterator as $oNode)
|
||||||
|
{
|
||||||
|
if ($oNode instanceof DisplayableGroupNode)
|
||||||
|
{
|
||||||
|
$aGroups[] = $oNode->GetObjects();
|
||||||
|
$oNode->SetProperty('group_index', $iGroupIdx);
|
||||||
|
$iGroupIdx++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$oP->AddTabContainer('Navigator');
|
$oP->AddTabContainer('Navigator');
|
||||||
$oP->SetCurrentTabContainer('Navigator');
|
$oP->SetCurrentTabContainer('Navigator');
|
||||||
@@ -1573,12 +1613,14 @@ EOF
|
|||||||
if ($sFirstTab == 'list')
|
if ($sFirstTab == 'list')
|
||||||
{
|
{
|
||||||
DisplayNavigatorListTab($oP, $aResults, $sRelation, $oObj);
|
DisplayNavigatorListTab($oP, $aResults, $sRelation, $oObj);
|
||||||
DisplayNavigatorGraphicsTab($oP, $aResults, $oRelGraph, $sClass, $id, $sRelation, $oAppContext, ($sDirection == 'down'));
|
DisplayNavigatorGraphicsTab($oP, $aResults, $oDisplayGraph, $sClass, $id, $sRelation, $oAppContext, ($sDirection == 'down'));
|
||||||
|
DisplayNavigatorGroupTab($oP, $aGroups, $sRelation, $oObj);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
DisplayNavigatorGraphicsTab($oP, $aResults, $oRelGraph, $sClass, $id, $sRelation, $oAppContext, ($sDirection == 'down'));
|
DisplayNavigatorGraphicsTab($oP, $aResults, $oDisplayGraph, $sClass, $id, $sRelation, $oAppContext, ($sDirection == 'down'));
|
||||||
DisplayNavigatorListTab($oP, $aResults, $sRelation, $oObj);
|
DisplayNavigatorListTab($oP, $aResults, $sRelation, $oObj);
|
||||||
|
DisplayNavigatorGroupTab($oP, $aGroups, $sRelation, $oObj);
|
||||||
}
|
}
|
||||||
|
|
||||||
$oP->SetCurrentTab('');
|
$oP->SetCurrentTab('');
|
||||||
|
|||||||
Reference in New Issue
Block a user