N°4479 - Impact analysis : Display and apply filter before display impact analysis graphical

This commit is contained in:
acognet
2022-01-11 07:33:20 +01:00
parent 24e6840a8b
commit c87de1024d
3 changed files with 1187 additions and 1165 deletions

View File

@@ -31,7 +31,7 @@ class DisplayableNode extends GraphNode
{ {
public $x; public $x;
public $y; public $y;
/** /**
* Create a new node inside a graph * Create a new node inside a graph
* @param SimpleGraph $oGraph * @param SimpleGraph $oGraph
@@ -51,27 +51,27 @@ class DisplayableNode extends GraphNode
{ {
return $this->GetProperty('icon_url', ''); return $this->GetProperty('icon_url', '');
} }
public function GetLabel() public function GetLabel()
{ {
return $this->GetProperty('label', $this->sId); return $this->GetProperty('label', $this->sId);
} }
public function GetWidth() public function GetWidth()
{ {
return max(32, 5*strlen($this->GetProperty('label'))); // approximation of the text's bounding box return max(32, 5*strlen($this->GetProperty('label'))); // approximation of the text's bounding box
} }
public function GetHeight() public function GetHeight()
{ {
return 32; return 32;
} }
public function Distance2(DisplayableNode $oNode) public function Distance2(DisplayableNode $oNode)
{ {
$dx = $this->x - $oNode->x; $dx = $this->x - $oNode->x;
$dy = $this->y - $oNode->y; $dy = $this->y - $oNode->y;
$d2 = $dx*$dx + $dy*$dy - $this->GetHeight()*$this->GetHeight(); $d2 = $dx*$dx + $dy*$dy - $this->GetHeight()*$this->GetHeight();
if ($d2 < 40) if ($d2 < 40)
{ {
@@ -79,12 +79,12 @@ class DisplayableNode extends GraphNode
} }
return $d2; return $d2;
} }
public function Distance(DisplayableNode $oNode) public function Distance(DisplayableNode $oNode)
{ {
return sqrt($this->Distance2($oNode)); return sqrt($this->Distance2($oNode));
} }
public function GetForRaphael($aContextDefs) public function GetForRaphael($aContextDefs)
{ {
$aNode = array(); $aNode = array();
@@ -100,7 +100,7 @@ class DisplayableNode extends GraphNode
$aNode['label'] = $this->GetLabel(); $aNode['label'] = $this->GetLabel();
$aNode['id'] = $this->GetId(); $aNode['id'] = $this->GetId();
$fOpacity = ($this->GetProperty('is_reached') ? 1 : 0.4); $fOpacity = ($this->GetProperty('is_reached') ? 1 : 0.4);
$aNode['icon_attr'] = array('opacity' => $fOpacity); $aNode['icon_attr'] = array('opacity' => $fOpacity);
$aNode['text_attr'] = array('opacity' => $fOpacity); $aNode['text_attr'] = array('opacity' => $fOpacity);
$aNode['tooltip'] = $this->GetTooltip($aContextDefs); $aNode['tooltip'] = $this->GetTooltip($aContextDefs);
$aNode['context_icons'] = array(); $aNode['context_icons'] = array();
@@ -114,7 +114,7 @@ class DisplayableNode extends GraphNode
} }
return $aNode; return $aNode;
} }
public function RenderAsPDF(iTopPDF $oPdf, DisplayableGraph $oGraph, $fScale, $aContextDefs) public function RenderAsPDF(iTopPDF $oPdf, DisplayableGraph $oGraph, $fScale, $aContextDefs)
{ {
$Alpha = 1.0; $Alpha = 1.0;
@@ -170,7 +170,7 @@ class DisplayableNode extends GraphNode
$oPdf->SetTextColor(0, 0, 0); $oPdf->SetTextColor(0, 0, 0);
$oPdf->Text($this->x*$fScale - $width/2, ($this->y + 18)*$fScale, $this->GetProperty('label')); $oPdf->Text($this->x*$fScale - $width/2, ($this->y + 18)*$fScale, $this->GetProperty('label'));
} }
/** /**
* Create a "whitened" version of the icon (retaining the transparency) to be used a background for masking the underlying lines * Create a "whitened" version of the icon (retaining the transparency) to be used a background for masking the underlying lines
* @param string $sIconFile The path to the file containing the icon * @param string $sIconFile The path to the file containing the icon
@@ -179,35 +179,35 @@ class DisplayableNode extends GraphNode
protected function CreateWhiteIcon(DisplayableGraph $oGraph, $sIconFile) protected function CreateWhiteIcon(DisplayableGraph $oGraph, $sIconFile)
{ {
$aInfo = getimagesize($sIconFile); $aInfo = getimagesize($sIconFile);
$im = null; $im = null;
switch($aInfo['mime']) switch($aInfo['mime'])
{ {
case 'image/png': case 'image/png':
if (function_exists('imagecreatefrompng')) if (function_exists('imagecreatefrompng'))
{ {
$im = imagecreatefrompng($sIconFile); $im = imagecreatefrompng($sIconFile);
} }
break; break;
case 'image/gif': case 'image/gif':
if (function_exists('imagecreatefromgif')) if (function_exists('imagecreatefromgif'))
{ {
$im = imagecreatefromgif($sIconFile); $im = imagecreatefromgif($sIconFile);
} }
break; break;
case 'image/jpeg': case 'image/jpeg':
case 'image/jpg': case 'image/jpg':
if (function_exists('imagecreatefromjpeg')) if (function_exists('imagecreatefromjpeg'))
{ {
$im = imagecreatefromjpeg($sIconFile); $im = imagecreatefromjpeg($sIconFile);
} }
break; break;
default: default:
return null; return null;
} }
if($im && imagefilter($im, IMG_FILTER_COLORIZE, 255, 255, 255)) if($im && imagefilter($im, IMG_FILTER_COLORIZE, 255, 255, 255))
{ {
@@ -222,17 +222,17 @@ class DisplayableNode extends GraphNode
return null; return null;
} }
} }
public function GetObjectCount() public function GetObjectCount()
{ {
return 1; return 1;
} }
public function GetObjectClass() public function GetObjectClass()
{ {
return is_object($this->GetProperty('object', null)) ? get_class($this->GetProperty('object', null)) : null; return is_object($this->GetProperty('object', null)) ? get_class($this->GetProperty('object', null)) : null;
} }
protected function AddToStats($oNode, &$aNodesPerClass) protected function AddToStats($oNode, &$aNodesPerClass)
{ {
$sClass = $oNode->GetObjectClass(); $sClass = $oNode->GetObjectClass();
@@ -256,9 +256,9 @@ class DisplayableNode extends GraphNode
{ {
$aNodesPerClass[$sClass][$sKey]['nodes'][$oNode->GetId()] = $oNode; $aNodesPerClass[$sClass][$sKey]['nodes'][$oNode->GetId()] = $oNode;
$aNodesPerClass[$sClass][$sKey]['count'] += $oNode->GetObjectCount(); $aNodesPerClass[$sClass][$sKey]['count'] += $oNode->GetObjectCount();
} }
} }
/** /**
* Retrieves the list of neighbour nodes, in the given direction: 'up' or 'down' * Retrieves the list of neighbour nodes, in the given direction: 'up' or 'down'
* @param bool $bDirectionDown * @param bool $bDirectionDown
@@ -279,11 +279,11 @@ class DisplayableNode extends GraphNode
foreach($this->GetIncomingEdges() as $oEdge) foreach($this->GetIncomingEdges() as $oEdge)
{ {
$aNextNodes[] = $oEdge->GetSourceNode(); $aNextNodes[] = $oEdge->GetSourceNode();
} }
} }
return $aNextNodes; return $aNextNodes;
} }
/** /**
* Replaces the next neighbour node (in the given direction: 'up' or 'down') by the supplied group node * Replaces the next neighbour node (in the given direction: 'up' or 'down') by the supplied group node
* preserving the connectivity of the graph * preserving the connectivity of the graph
@@ -351,7 +351,7 @@ class DisplayableNode extends GraphNode
} }
} }
} }
if ($oGraph->GetNode($oNextNode->GetId())) if ($oGraph->GetNode($oNextNode->GetId()))
{ {
$oGraph->_RemoveNode($oNextNode); $oGraph->_RemoveNode($oNextNode);
@@ -367,9 +367,9 @@ class DisplayableNode extends GraphNode
{ {
$oNewNode->AddObject($oNextNode->GetProperty('object')); $oNewNode->AddObject($oNextNode->GetProperty('object'));
} }
} }
} }
/** /**
* Group together (as a special kind of nodes) all the similar neighbours of the current node * Group together (as a special kind of nodes) all the similar neighbours of the current node
* @param DisplayableGraph $oGraph * @param DisplayableGraph $oGraph
@@ -381,7 +381,7 @@ class DisplayableNode extends GraphNode
{ {
if ($this->GetProperty('grouped') === true) return; if ($this->GetProperty('grouped') === true) return;
$this->SetProperty('grouped', true); $this->SetProperty('grouped', true);
$aNodesPerClass = array(); $aNodesPerClass = array();
foreach($this->GetNextNodes($bDirectionDown) as $oNode) foreach($this->GetNextNodes($bDirectionDown) as $oNode)
{ {
@@ -412,7 +412,7 @@ class DisplayableNode extends GraphNode
$oNewNode->SetProperty('is_reached', ($sStatus == 'reached')); $oNewNode->SetProperty('is_reached', ($sStatus == 'reached'));
$oNewNode->SetProperty('count', $aGroupProps['count']); $oNewNode->SetProperty('count', $aGroupProps['count']);
} }
try try
{ {
if ($bDirectionDown) if ($bDirectionDown)
@@ -427,8 +427,8 @@ class DisplayableNode extends GraphNode
catch(Exception $e) catch(Exception $e)
{ {
// Ignore this redundant egde // Ignore this redundant egde
} }
foreach($aGroupProps['nodes'] as $oNextNode) foreach($aGroupProps['nodes'] as $oNextNode)
{ {
$this->ReplaceNextNodeBy($oGraph, $oNextNode, $oNewNode, $bDirectionDown); $this->ReplaceNextNodeBy($oGraph, $oNextNode, $oNewNode, $bDirectionDown);
@@ -445,7 +445,7 @@ class DisplayableNode extends GraphNode
} }
} }
} }
public function GetTooltip($aContextDefs) public function GetTooltip($aContextDefs)
{ {
$sHtml = ''; $sHtml = '';
@@ -474,9 +474,9 @@ class DisplayableNode extends GraphNode
$sHtml .= '<tr><td>'.$oAttDef->GetLabel().':&nbsp;</td><td>'.$oCurrObj->GetAsHtml($sAttCode).'</td></tr>'; $sHtml .= '<tr><td>'.$oAttDef->GetLabel().':&nbsp;</td><td>'.$oCurrObj->GetAsHtml($sAttCode).'</td></tr>';
} }
$sHtml .= '</tbody></table>'; $sHtml .= '</tbody></table>';
return $sHtml; return $sHtml;
} }
/** /**
* Get the description of the node in "dot" language * Get the description of the node in "dot" language
* Used to generate the positions in the graph, but we'd better use fake label * Used to generate the positions in the graph, but we'd better use fake label
@@ -508,7 +508,7 @@ class DisplayableRedundancyNode extends DisplayableNode
{ {
return 24; return 24;
} }
public function GetForRaphael($aContextDefs) public function GetForRaphael($aContextDefs)
{ {
$aNode = array(); $aNode = array();
@@ -519,7 +519,7 @@ class DisplayableRedundancyNode extends DisplayableNode
$aNode['x'] = $this->x; $aNode['x'] = $this->x;
$aNode['y']= $this->y; $aNode['y']= $this->y;
$aNode['label'] = $this->GetLabel(); $aNode['label'] = $this->GetLabel();
$aNode['id'] = $this->GetId(); $aNode['id'] = $this->GetId();
$fDiscOpacity = ($this->GetProperty('is_reached') ? 1 : 0.2); $fDiscOpacity = ($this->GetProperty('is_reached') ? 1 : 0.2);
$sColor = ($this->GetProperty('is_reached_count') > $this->GetProperty('threshold')) ? '#c33' : '#999'; $sColor = ($this->GetProperty('is_reached_count') > $this->GetProperty('threshold')) ? '#c33' : '#999';
$aNode['disc_attr'] = array('stroke-width' => 2, 'stroke' => '#000', 'fill' => $sColor, 'opacity' => $fDiscOpacity); $aNode['disc_attr'] = array('stroke-width' => 2, 'stroke' => '#000', 'fill' => $sColor, 'opacity' => $fDiscOpacity);
@@ -550,35 +550,35 @@ class DisplayableRedundancyNode extends DisplayableNode
$height = $oPdf->GetStringHeight(1000, $sLabel); $height = $oPdf->GetStringHeight(1000, $sLabel);
$xPos = (float)$this->x*$fScale - $width/2; $xPos = (float)$this->x*$fScale - $width/2;
$yPos = (float)$this->y*$fScale - $height/2; $yPos = (float)$this->y*$fScale - $height/2;
$oPdf->SetXY(($this->x - 16)*$fScale, ($this->y - 16)*$fScale); $oPdf->SetXY(($this->x - 16)*$fScale, ($this->y - 16)*$fScale);
$oPdf->Cell(32*$fScale, 32*$fScale, $sLabel, 0, 0, 'C', 0, '', 0, false, 'T', 'C'); $oPdf->Cell(32*$fScale, 32*$fScale, $sLabel, 0, 0, 'C', 0, '', 0, false, 'T', 'C');
} }
/** /**
* @see DisplayableNode::GroupSimilarNeighbours() * @see DisplayableNode::GroupSimilarNeighbours()
*/ */
public function GroupSimilarNeighbours(DisplayableGraph $oGraph, $iThresholdCount, $bDirectionUp = false, $bDirectionDown = true) public function GroupSimilarNeighbours(DisplayableGraph $oGraph, $iThresholdCount, $bDirectionUp = false, $bDirectionDown = true)
{ {
parent::GroupSimilarNeighbours($oGraph, $iThresholdCount, $bDirectionUp, $bDirectionDown); parent::GroupSimilarNeighbours($oGraph, $iThresholdCount, $bDirectionUp, $bDirectionDown);
if ($bDirectionUp) if ($bDirectionUp)
{ {
$aNodesPerClass = array(); $aNodesPerClass = array();
foreach($this->GetIncomingEdges() as $oEdge) foreach($this->GetIncomingEdges() as $oEdge)
{ {
$oNode = $oEdge->GetSourceNode(); $oNode = $oEdge->GetSourceNode();
if (($oNode->GetObjectClass() !== null) && (!$oNode->GetProperty('is_reached'))) if (($oNode->GetObjectClass() !== null) && (!$oNode->GetProperty('is_reached')))
{ {
$this->AddToStats($oNode, $aNodesPerClass); $this->AddToStats($oNode, $aNodesPerClass);
} }
else else
{ {
//$oNode->GroupSimilarNeighbours($oGraph, $iThresholdCount, $bDirectionUp, $bDirectionDown); //$oNode->GroupSimilarNeighbours($oGraph, $iThresholdCount, $bDirectionUp, $bDirectionDown);
} }
} }
foreach($aNodesPerClass as $sClass => $aDefs) foreach($aNodesPerClass as $sClass => $aDefs)
{ {
foreach($aDefs as $sStatus => $aGroupProps) foreach($aDefs as $sStatus => $aGroupProps)
@@ -591,8 +591,8 @@ class DisplayableRedundancyNode extends DisplayableNode
$oNewNode->SetProperty('is_reached', ($sStatus == 'is_reached')); $oNewNode->SetProperty('is_reached', ($sStatus == 'is_reached'));
$oNewNode->SetProperty('class', $sClass); $oNewNode->SetProperty('class', $sClass);
$oNewNode->SetProperty('count', count($aGroupProps['nodes'])); $oNewNode->SetProperty('count', count($aGroupProps['nodes']));
$sNewId = $this->GetId().'::'.$sClass.'/'.(($sStatus == 'reached') ? '_reached': ''); $sNewId = $this->GetId().'::'.$sClass.'/'.(($sStatus == 'reached') ? '_reached': '');
$oNewNode = $oGraph->GetNode($sNewId); $oNewNode = $oGraph->GetNode($sNewId);
if ($oNewNode == null) if ($oNewNode == null)
@@ -604,7 +604,7 @@ class DisplayableRedundancyNode extends DisplayableNode
$oNewNode->SetProperty('is_reached', ($sStatus == 'reached')); $oNewNode->SetProperty('is_reached', ($sStatus == 'reached'));
$oNewNode->SetProperty('count', $aGroupProps['count']); $oNewNode->SetProperty('count', $aGroupProps['count']);
} }
try try
{ {
$oOutgoingEdge = new DisplayableEdge($oGraph, '-'.$this->GetId().'-'.$oNewNode->GetId().'/'.$sStatus, $oNewNode, $this); $oOutgoingEdge = new DisplayableEdge($oGraph, '-'.$this->GetId().'-'.$oNewNode->GetId().'/'.$sStatus, $oNewNode, $this);
@@ -613,7 +613,7 @@ class DisplayableRedundancyNode extends DisplayableNode
{ {
// Ignore this redundant egde // Ignore this redundant egde
} }
foreach($aGroupProps['nodes'] as $oNextNode) foreach($aGroupProps['nodes'] as $oNextNode)
{ {
$this->ReplaceNextNodeBy($oGraph, $oNextNode, $oNewNode, !$bDirectionUp); $this->ReplaceNextNodeBy($oGraph, $oNextNode, $oNewNode, !$bDirectionUp);
@@ -631,7 +631,7 @@ class DisplayableRedundancyNode extends DisplayableNode
} }
} }
} }
public function GetTooltip($aContextDefs) public function GetTooltip($aContextDefs)
{ {
$sHtml = ''; $sHtml = '';
@@ -640,9 +640,9 @@ class DisplayableRedundancyNode extends DisplayableNode
$sHtml .= "<tr><td>".Dict::Format('UI:RelationTooltip:ImpactedItems_N_of_M' , $this->GetProperty('is_reached_count'), $this->GetProperty('min_up') + $this->GetProperty('threshold'))."</td></tr>"; $sHtml .= "<tr><td>".Dict::Format('UI:RelationTooltip:ImpactedItems_N_of_M' , $this->GetProperty('is_reached_count'), $this->GetProperty('min_up') + $this->GetProperty('threshold'))."</td></tr>";
$sHtml .= "<tr><td>".Dict::Format('UI:RelationTooltip:CriticalThreshold_N_of_M' , $this->GetProperty('threshold'), $this->GetProperty('min_up') + $this->GetProperty('threshold'))."</td></tr>"; $sHtml .= "<tr><td>".Dict::Format('UI:RelationTooltip:CriticalThreshold_N_of_M' , $this->GetProperty('threshold'), $this->GetProperty('min_up') + $this->GetProperty('threshold'))."</td></tr>";
$sHtml .= '</tbody></table>'; $sHtml .= '</tbody></table>';
return $sHtml; return $sHtml;
} }
public function GetObjectCount() public function GetObjectCount()
{ {
@@ -666,7 +666,7 @@ class DisplayableEdge extends GraphEdge
} }
$xStart = $oSourceNode->x * $fScale; $xStart = $oSourceNode->x * $fScale;
$yStart = $oSourceNode->y * $fScale; $yStart = $oSourceNode->y * $fScale;
$oSinkNode = $this->GetSinkNode(); $oSinkNode = $this->GetSinkNode();
if (($oSinkNode->x == null) || ($oSinkNode->y == null)) if (($oSinkNode->x == null) || ($oSinkNode->y == null))
{ {
@@ -674,9 +674,9 @@ class DisplayableEdge extends GraphEdge
} }
$xEnd = $oSinkNode->x * $fScale; $xEnd = $oSinkNode->x * $fScale;
$yEnd = $oSinkNode->y * $fScale; $yEnd = $oSinkNode->y * $fScale;
$bReached = ($this->GetSourceNode()->GetProperty('is_reached') && $this->GetSinkNode()->GetProperty('is_reached')); $bReached = ($this->GetSourceNode()->GetProperty('is_reached') && $this->GetSinkNode()->GetProperty('is_reached'));
$oPdf->setAlpha(1); $oPdf->setAlpha(1);
if ($bReached) if ($bReached)
{ {
@@ -688,8 +688,8 @@ class DisplayableEdge extends GraphEdge
} }
$oPdf->SetLineStyle(array('width' => 2*$fScale, 'cap' => 'round', 'join' => 'miter', 'dash' => 0, 'color' => $aColor)); $oPdf->SetLineStyle(array('width' => 2*$fScale, 'cap' => 'round', 'join' => 'miter', 'dash' => 0, 'color' => $aColor));
$oPdf->Line($xStart, $yStart, $xEnd, $yEnd); $oPdf->Line($xStart, $yStart, $xEnd, $yEnd);
$vx = $xEnd - $xStart; $vx = $xEnd - $xStart;
$vy = $yEnd - $yStart; $vy = $yEnd - $yStart;
$l = sqrt($vx*$vx + $vy*$vy); $l = sqrt($vx*$vx + $vy*$vy);
@@ -699,24 +699,24 @@ class DisplayableEdge extends GraphEdge
$uy = $vx; $uy = $vx;
$lPos = max($l/2, $l - 40*$fScale); $lPos = max($l/2, $l - 40*$fScale);
$iArrowSize = 5*$fScale; $iArrowSize = 5*$fScale;
$x = $xStart + $lPos * $vx; $x = $xStart + $lPos * $vx;
$y = $yStart + $lPos * $vy; $y = $yStart + $lPos * $vy;
$oPdf->Line($x, $y, $x + $iArrowSize * ($ux-$vx), $y + $iArrowSize * ($uy-$vy)); $oPdf->Line($x, $y, $x + $iArrowSize * ($ux-$vx), $y + $iArrowSize * ($uy-$vy));
$oPdf->Line($x, $y, $x - $iArrowSize * ($ux+$vx), $y - $iArrowSize * ($uy+$vy)); $oPdf->Line($x, $y, $x - $iArrowSize * ($ux+$vx), $y - $iArrowSize * ($uy+$vy));
} }
} }
class DisplayableGroupNode extends DisplayableNode class DisplayableGroupNode extends DisplayableNode
{ {
protected $aObjects; protected $aObjects;
public function __construct(SimpleGraph $oGraph, $sId, $x = 0, $y = 0) public function __construct(SimpleGraph $oGraph, $sId, $x = 0, $y = 0)
{ {
parent::__construct($oGraph, $sId, $x, $y); parent::__construct($oGraph, $sId, $x, $y);
$this->aObjects = array(); $this->aObjects = array();
} }
public function AddObject(DBObject $oObj = null) public function AddObject(DBObject $oObj = null)
{ {
if (is_object($oObj)) if (is_object($oObj))
@@ -729,12 +729,12 @@ class DisplayableGroupNode extends DisplayableNode
$this->aObjects[$oObj->GetKey()] = $oObj; $this->aObjects[$oObj->GetKey()] = $oObj;
} }
} }
public function GetObjects() public function GetObjects()
{ {
return $this->aObjects; return $this->aObjects;
} }
public function GetWidth() public function GetWidth()
{ {
return 50; return 50;
@@ -760,7 +760,7 @@ class DisplayableGroupNode extends DisplayableNode
$aNode['tooltip'] = $this->GetTooltip($aContextDefs); $aNode['tooltip'] = $this->GetTooltip($aContextDefs);
return $aNode; return $aNode;
} }
public function RenderAsPDF(iTopPDF $oPdf, DisplayableGraph $oGraph, $fScale, $aContextDefs) public function RenderAsPDF(iTopPDF $oPdf, DisplayableGraph $oGraph, $fScale, $aContextDefs)
{ {
$bReached = $this->GetProperty('is_reached'); $bReached = $this->GetProperty('is_reached');
@@ -790,7 +790,7 @@ class DisplayableGroupNode extends DisplayableNode
$oPdf->SetTextColor(0, 0, 0); $oPdf->SetTextColor(0, 0, 0);
$oPdf->Text($this->x * $fScale - $width / 2, ($this->y + 25) * $fScale, $this->GetProperty('label')); $oPdf->Text($this->x * $fScale - $width / 2, ($this->y + 25) * $fScale, $this->GetProperty('label'));
} }
public function GetTooltip($aContextDefs) public function GetTooltip($aContextDefs)
{ {
$iGroupIdx = $this->GetProperty('group_index'); $iGroupIdx = $this->GetProperty('group_index');
@@ -823,7 +823,7 @@ class DisplayableGraph extends SimpleGraph
protected $aTempImages; protected $aTempImages;
protected $aSourceObjects; protected $aSourceObjects;
protected $aSinkObjects; protected $aSinkObjects;
public function __construct() public function __construct()
{ {
parent::__construct(); parent::__construct();
@@ -831,14 +831,14 @@ class DisplayableGraph extends SimpleGraph
$this->aSourceObjects = array(); $this->aSourceObjects = array();
$this->aSinkObjects = array(); $this->aSinkObjects = array();
} }
public function GetTempImageName() public function GetTempImageName()
{ {
$sNewTempName = tempnam(APPROOT.'data', 'img-'); $sNewTempName = tempnam(APPROOT.'data', 'img-');
$this->aTempImages[] = $sNewTempName; $this->aTempImages[] = $sNewTempName;
return $sNewTempName; return $sNewTempName;
} }
public function __destruct() public function __destruct()
{ {
foreach($this->aTempImages as $sTempFile) foreach($this->aTempImages as $sTempFile)
@@ -918,7 +918,7 @@ class DisplayableGraph extends SimpleGraph
$oSinkNode = $oNewGraph->GetNode($oEdge->GetSinkNode()->GetId()); $oSinkNode = $oNewGraph->GetNode($oEdge->GetSinkNode()->GetId());
$oNewEdge = new DisplayableEdge($oNewGraph, $oEdge->GetId(), $oSourceNode, $oSinkNode); $oNewEdge = new DisplayableEdge($oNewGraph, $oEdge->GetId(), $oSourceNode, $oSinkNode);
} }
// Remove duplicate edges between two nodes // Remove duplicate edges between two nodes
$oEdgesIter = new RelationTypeIterator($oNewGraph, 'Edge'); $oEdgesIter = new RelationTypeIterator($oNewGraph, 'Edge');
$aEdgeKeys = array(); $aEdgeKeys = array();
@@ -946,7 +946,7 @@ class DisplayableGraph extends SimpleGraph
} }
} }
} }
$oNodesIter = new RelationTypeIterator($oNewGraph, 'Node'); $oNodesIter = new RelationTypeIterator($oNewGraph, 'Node');
foreach($oNodesIter as $oNode) foreach($oNodesIter as $oNode)
{ {
@@ -981,7 +981,7 @@ class DisplayableGraph extends SimpleGraph
} }
} }
} }
// Remove duplicate edges between two nodes // Remove duplicate edges between two nodes
$oEdgesIter = new RelationTypeIterator($oNewGraph, 'Edge'); $oEdgesIter = new RelationTypeIterator($oNewGraph, 'Edge');
$aEdgeKeys = array(); $aEdgeKeys = array();
@@ -1010,10 +1010,10 @@ class DisplayableGraph extends SimpleGraph
} }
} }
set_time_limit(intval($iPreviousTimeLimit)); set_time_limit(intval($iPreviousTimeLimit));
return $oNewGraph; return $oNewGraph;
} }
/** /**
* Initializes the positions by rendering using Graphviz in xdot format * Initializes the positions by rendering using Graphviz in xdot format
* and parsing the output. * and parsing the output.
@@ -1026,7 +1026,7 @@ class DisplayableGraph extends SimpleGraph
{ {
throw new Exception($sDot); throw new Exception($sDot);
} }
$aChunks = explode(";", $sDot); $aChunks = explode(";", $sDot);
foreach($aChunks as $sChunk) foreach($aChunks as $sChunk)
{ {
@@ -1035,7 +1035,7 @@ class DisplayableGraph extends SimpleGraph
$sId = $aMatches[1]; $sId = $aMatches[1];
$xPos = $aMatches[2]; $xPos = $aMatches[2];
$yPos = $aMatches[3]; $yPos = $aMatches[3];
$oNode = $this->GetNode($sId); $oNode = $this->GetNode($sId);
if ($oNode !== null) if ($oNode !== null)
{ {
@@ -1049,7 +1049,7 @@ class DisplayableGraph extends SimpleGraph
} }
} }
} }
public function GetBoundingBox() public function GetBoundingBox()
{ {
$xMin = null; $xMin = null;
@@ -1074,10 +1074,10 @@ class DisplayableGraph extends SimpleGraph
$yMax = max($yMax, $oNode->y + $oNode->GetHeight() / 2); $yMax = max($yMax, $oNode->y + $oNode->GetHeight() / 2);
} }
} }
return array('xmin' => $xMin, 'xmax' => $xMax, 'ymin' => $yMin, 'ymax' => $yMax); return array('xmin' => $xMin, 'xmax' => $xMax, 'ymin' => $yMin, 'ymax' => $yMax);
} }
function Translate($dx, $dy) function Translate($dx, $dy)
{ {
$oIterator = new RelationTypeIterator($this, 'Node'); $oIterator = new RelationTypeIterator($this, 'Node');
@@ -1085,9 +1085,9 @@ class DisplayableGraph extends SimpleGraph
{ {
$oNode->x += $dx; $oNode->x += $dx;
$oNode->y += $dy; $oNode->y += $dy;
} }
} }
public function UpdatePositions($aPositions) public function UpdatePositions($aPositions)
{ {
foreach($aPositions as $sNodeId => $aPos) foreach($aPositions as $sNodeId => $aPos)
@@ -1107,7 +1107,7 @@ class DisplayableGraph extends SimpleGraph
function GetAsJSON($sContextKey) function GetAsJSON($sContextKey)
{ {
$aContextDefs = static::GetContextDefinitions($sContextKey, false); $aContextDefs = static::GetContextDefinitions($sContextKey, false);
$aData = array('nodes' => array(), 'edges' => array(), 'groups' => array(), 'lists' => array()); $aData = array('nodes' => array(), 'edges' => array(), 'groups' => array(), 'lists' => array());
$iGroupIdx = 0; $iGroupIdx = 0;
$oIterator = new RelationTypeIterator($this, 'Node'); $oIterator = new RelationTypeIterator($this, 'Node');
@@ -1131,7 +1131,7 @@ class DisplayableGraph extends SimpleGraph
$aData['groups'][$iGroupIdx] = array('class' => $sClass, 'keys' => $aKeys); $aData['groups'][$iGroupIdx] = array('class' => $sClass, 'keys' => $aKeys);
$oNode->SetProperty('group_index', $iGroupIdx); $oNode->SetProperty('group_index', $iGroupIdx);
$iGroupIdx++; $iGroupIdx++;
if ($oNode->GetProperty('is_reached')) if ($oNode->GetProperty('is_reached'))
{ {
// Also add the objects from this group into the 'list' tab // Also add the objects from this group into the 'list' tab
@@ -1139,11 +1139,11 @@ class DisplayableGraph extends SimpleGraph
{ {
$aData['lists'][$sClass] = $aKeys; $aData['lists'][$sClass] = $aKeys;
} }
else else
{ {
$aData['lists'][$sClass] = array_merge($aData['lists'][$sClass], $aKeys); $aData['lists'][$sClass] = array_merge($aData['lists'][$sClass], $aKeys);
} }
} }
} }
if (($oNode instanceof DisplayableNode) && $oNode->GetProperty('is_reached') && is_object($oNode->GetProperty('object'))) if (($oNode instanceof DisplayableNode) && $oNode->GetProperty('is_reached') && is_object($oNode->GetProperty('object')))
@@ -1157,9 +1157,9 @@ class DisplayableGraph extends SimpleGraph
} }
$aData['nodes'][] = $oNode->GetForRaphael($aContextDefs); $aData['nodes'][] = $oNode->GetForRaphael($aContextDefs);
} }
uksort($aData['lists'], array(get_class($this), 'SortOnClassLabel')); // sort on the localized names of the classes to provide a consistent and stable order uksort($aData['lists'], array(get_class($this), 'SortOnClassLabel')); // sort on the localized names of the classes to provide a consistent and stable order
$oIterator = new RelationTypeIterator($this, 'Edge'); $oIterator = new RelationTypeIterator($this, 'Edge');
foreach($oIterator as $sId => $oEdge) foreach($oIterator as $sId => $oEdge)
{ {
@@ -1171,7 +1171,7 @@ class DisplayableGraph extends SimpleGraph
$aEdge['attr'] = array('opacity' => $fOpacity, 'stroke' => '#000'); $aEdge['attr'] = array('opacity' => $fOpacity, 'stroke' => '#000');
$aData['edges'][] = $aEdge; $aData['edges'][] = $aEdge;
} }
return json_encode($aData); return json_encode($aData);
} }
@@ -1200,12 +1200,12 @@ class DisplayableGraph extends SimpleGraph
{ {
$aContextDefs = static::GetContextDefinitions($sContextKey, false); // No need to develop the parameters $aContextDefs = static::GetContextDefinitions($sContextKey, false); // No need to develop the parameters
$oPdf = $oPage->get_tcpdf(); $oPdf = $oPage->get_tcpdf();
$aBB = $this->GetBoundingBox(); $aBB = $this->GetBoundingBox();
$this->Translate(-$aBB['xmin'], -$aBB['ymin']); $this->Translate(-$aBB['xmin'], -$aBB['ymin']);
$aMargins = $oPdf->getMargins(); $aMargins = $oPdf->getMargins();
if ($xMin == -1) if ($xMin == -1)
{ {
$xMin = $aMargins['left']; $xMin = $aMargins['left'];
@@ -1222,7 +1222,7 @@ class DisplayableGraph extends SimpleGraph
{ {
$yMax = $oPdf->getPageHeight() - $aMargins['bottom']; $yMax = $oPdf->getPageHeight() - $aMargins['bottom'];
} }
$fBreakMargin = $oPdf->getBreakMargin(); $fBreakMargin = $oPdf->getBreakMargin();
$oPdf->SetAutoPageBreak(false); $oPdf->SetAutoPageBreak(false);
$aRemainingArea = $this->RenderKey($oPdf, $sComments, $xMin, $yMin, $xMax, $yMax, $aContextDefs); $aRemainingArea = $this->RenderKey($oPdf, $sComments, $xMin, $yMin, $xMax, $yMax, $aContextDefs);
@@ -1230,19 +1230,19 @@ class DisplayableGraph extends SimpleGraph
$xMax = $aRemainingArea['xmax']; $xMax = $aRemainingArea['xmax'];
$yMin = $aRemainingArea['ymin']; $yMin = $aRemainingArea['ymin'];
$yMax = $aRemainingArea['ymax']; $yMax = $aRemainingArea['ymax'];
//$oPdf->Rect($xMin, $yMin, $xMax - $xMin, $yMax - $yMin, 'D', array(), array(225, 50, 50)); //$oPdf->Rect($xMin, $yMin, $xMax - $xMin, $yMax - $yMin, 'D', array(), array(225, 50, 50));
$fPageW = $xMax - $xMin; $fPageW = $xMax - $xMin;
$fPageH = $yMax - $yMin; $fPageH = $yMax - $yMin;
$w = $aBB['xmax'] - $aBB['xmin']; $w = $aBB['xmax'] - $aBB['xmin'];
$h = $aBB['ymax'] - $aBB['ymin'] + 10; // Extra space for the labels which may appear "below" the icons $h = $aBB['ymax'] - $aBB['ymin'] + 10; // Extra space for the labels which may appear "below" the icons
$fScale = min($fPageW / $w, $fPageH / $h); $fScale = min($fPageW / $w, $fPageH / $h);
$dx = ($fPageW - $fScale * $w) / 2; $dx = ($fPageW - $fScale * $w) / 2;
$dy = ($fPageH - $fScale * $h) / 2; $dy = ($fPageH - $fScale * $h) / 2;
$this->Translate(($xMin + $dx)/$fScale, ($yMin + $dy)/$fScale); $this->Translate(($xMin + $dx)/$fScale, ($yMin + $dy)/$fScale);
$oIterator = new RelationTypeIterator($this, 'Edge'); $oIterator = new RelationTypeIterator($this, 'Edge');
@@ -1264,7 +1264,7 @@ class DisplayableGraph extends SimpleGraph
$oPdf->SetAlpha(1); $oPdf->SetAlpha(1);
$oPdf->SetTextColor(0, 0, 0); $oPdf->SetTextColor(0, 0, 0);
} }
/** /**
* Renders (in PDF) the key (legend) of the graphics vertically to the left of the specified zone (xmin,ymin, xmax,ymax), * Renders (in PDF) the key (legend) of the graphics vertically to the left of the specified zone (xmin,ymin, xmax,ymax),
* and the comment (if any) at the bottom of the page. Returns the position of remaining area. * and the comment (if any) at the bottom of the page. Returns the position of remaining area.
@@ -1332,7 +1332,7 @@ class DisplayableGraph extends SimpleGraph
$yPos += $fIconSize + 2 * $fPadding; $yPos += $fIconSize + 2 * $fPadding;
} }
$oPdf->Rect($xMin, $yMin, $fMaxWidth + $fIconSize + 3*$fPadding, $yMax - $yMin, 'D'); $oPdf->Rect($xMin, $yMin, $fMaxWidth + $fIconSize + 3*$fPadding, $yMax - $yMin, 'D');
if ($sComments != '') if ($sComments != '')
{ {
// Draw the comment text (surrounded by a rectangle) // Draw the comment text (surrounded by a rectangle)
@@ -1347,10 +1347,10 @@ class DisplayableGraph extends SimpleGraph
$oPdf->Rect($xPos, $yPos, $w + 2*$fPadding, $h + 2*$fPadding, 'D'); $oPdf->Rect($xPos, $yPos, $w + 2*$fPadding, $h + 2*$fPadding, 'D');
$yMax = $yPos - $fPadding; $yMax = $yPos - $fPadding;
} }
return array('xmin' => $xMin + $fMaxWidth + $fIconSize + 4*$fPadding, 'xmax' => $xMax, 'ymin' => $yMin, 'ymax' => $yMax); return array('xmin' => $xMin + $fMaxWidth + $fIconSize + 4*$fPadding, 'xmax' => $xMax, 'ymin' => $yMin, 'ymax' => $yMax);
} }
/** /**
* Get the context definitions from the parameters / configuration. The format of the "key" string is: * Get the context definitions from the parameters / configuration. The format of the "key" string is:
* <module>/relation_context/<class>/<relation>/<direction> * <module>/relation_context/<class>/<relation>/<direction>
@@ -1372,7 +1372,7 @@ class DisplayableGraph extends SimpleGraph
else else
{ {
$sLeafClass = $aLevels[2]; $sLeafClass = $aLevels[2];
if (!MetaModel::IsValidClass($sLeafClass)) if (!MetaModel::IsValidClass($sLeafClass))
{ {
IssueLog::Warning("GetContextDefinitions: invalid 'sLeafClass' = '$sLeafClass'. A valid class name is expected in 3rd position inside '$sContextKey' !"); IssueLog::Warning("GetContextDefinitions: invalid 'sLeafClass' = '$sLeafClass'. A valid class name is expected in 3rd position inside '$sContextKey' !");
@@ -1387,7 +1387,7 @@ class DisplayableGraph extends SimpleGraph
$aContextDefs = array_merge($aContextDefs, $aRelationContext[$sClass][$aLevels[3]][$aLevels[4]]['items']); $aContextDefs = array_merge($aContextDefs, $aRelationContext[$sClass][$aLevels[3]][$aLevels[4]]['items']);
} }
} }
// Check if the queries are valid // Check if the queries are valid
foreach($aContextDefs as $sKey => $sDefs) foreach($aContextDefs as $sKey => $sDefs)
{ {
@@ -1425,168 +1425,101 @@ class DisplayableGraph extends SimpleGraph
* @throws \CoreException * @throws \CoreException
* @throws \DictExceptionMissingString * @throws \DictExceptionMissingString
*/ */
function Display(WebPage $oP, $aResults, $sRelation, ApplicationContext $oAppContext, $aExcludedObjects, $sObjClass, $iObjKey, $sContextKey, $aContextParams = array()) function Display(WebPage $oP, $aResults, $sRelation, ApplicationContext $oAppContext, $aExcludedObjects, $sObjClass, $iObjKey, $sContextKey, $aContextParams = array(), bool $sLazyLoading = false)
{ {
$aContextDefs = static::GetContextDefinitions($sContextKey, true, $aContextParams); list($aExcludedByClass, $aAdditionalContexts) = $this->DisplayFiltering($sContextKey, $aContextParams, $aExcludedObjects, $oP, $aResults, $sLazyLoading);
$aExcludedByClass = array();
foreach($aExcludedObjects as $oObj)
{
if (!array_key_exists(get_class($oObj), $aExcludedByClass))
{
$aExcludedByClass[get_class($oObj)] = array();
}
$aExcludedByClass[get_class($oObj)][] = $oObj->GetKey();
}
$sSftShort = Dict::S('UI:ElementsDisplayed');
$sSearchToggle = Dict::S('UI:Search:Toggle');
$oP->add("<div class=\"not-printable\">\n");
$oUiSearchBlock = new Panel($sSftShort, [],Panel::ENUM_COLOR_SCHEME_CYAN, 'ds_flash');
$oUiSearchBlock->SetCSSClasses(["ibo-search-form-panel", "display_block"]);
$oUiSearchBlock->SetIsCollapsible(true);
$oUiHtmlBlock = new Combodo\iTop\Application\UI\Base\Component\Html\Html(
<<<EOF
<div id="ds_flash" class="search_box ibo-display-graph--search-box">
<div id="dh_flash_criterion_outer" class="sf_criterion_area"><div class="sf_criterion_row">
EOF
);
$oP->add_ready_script(
<<<EOF
$("#dh_flash > .sf_title").on('click', function() {
$("#dh_flash").toggleClass('closed');
});
$('#ReloadMovieBtn').button().button('disable');
EOF
);
$aSortedElements = array();
foreach($aResults as $sClassIdx => $aObjects)
{
foreach($aObjects as $oCurrObj)
{
$sSubClass = get_class($oCurrObj);
$aSortedElements[$sSubClass] = MetaModel::GetName($sSubClass);
}
}
asort($aSortedElements);
$idx = 0;
foreach($aSortedElements as $sSubClass => $sClassName)
{
$oUiHtmlBlock->AddHtml("<div><input type=\"checkbox\" id=\"exclude_$idx\" name=\"excluded[]\" value=\"$sSubClass\" checked onChange=\"$('#ReloadMovieBtn').button('enable')\"><label for=\"exclude_$idx\">");
$oUiMedallionBlock= new MedallionIcon(MetaModel::GetClassIcon($sSubClass, false));
$oUiMedallionBlock->SetDescription($sClassName);
$oUiHtmlBlock->AddHtml(BlockRenderer::RenderBlockTemplates($oUiMedallionBlock));
$oUiHtmlBlock->AddHtml("</label></div>");
$idx++;
}
$oUiHtmlBlock->AddHtml("</div>");
$oUiHtmlBlock->AddHtml("<button type=\"button\" id=\"ReloadMovieBtn\" class=\"ibo-button ibo-is-neutral ibo-is-regular\" onClick=\"DoReload()\">".Dict::S('UI:Button:Refresh')."</button></div></form>");
$oUiHtmlBlock->AddHtml("</div>\n");
$oUiHtmlBlock->AddHtml("</div>\n"); // class="not-printable"
$oUiSearchBlock->AddSubBlock($oUiHtmlBlock);
$oP->AddUiBlock($oUiSearchBlock);
$aAdditionalContexts = array();
foreach($aContextDefs as $sKey => $aDefinition)
{
$aAdditionalContexts[] = array('key' => $sKey, 'label' => Dict::S($aDefinition['dict']), 'oql' => $aDefinition['oql'], 'default' => (array_key_exists('default', $aDefinition) && ($aDefinition['default'] == 'yes')));
}
$sDirection = utils::ReadParam('d', 'horizontal');
$iGroupingThreshold = utils::ReadParam('g', 5); $iGroupingThreshold = utils::ReadParam('g', 5);
WebResourcesHelper::EnableSimpleGraphInWebPage($oP); WebResourcesHelper::EnableSimpleGraphInWebPage($oP);
try try {
{
$this->InitFromGraphviz(); $this->InitFromGraphviz();
$sExportAsPdfURL = '';
$sExportAsPdfURL = utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php?operation=relation_pdf&relation='.$sRelation.'&direction='.($this->bDirectionDown ? 'down' : 'up'); $sExportAsPdfURL = utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php?operation=relation_pdf&relation='.$sRelation.'&direction='.($this->bDirectionDown ? 'down' : 'up');
$oAppcontext = new ApplicationContext();
$sContext = $oAppContext->GetForLink(); $sContext = $oAppContext->GetForLink();
$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 = utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php?operation=relation_attachment&relation='.$sRelation.'&direction='.($this->bDirectionDown ? 'down' : 'up'); $sExportAsDocumentURL = utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php?operation=relation_attachment&relation='.$sRelation.'&direction='.($this->bDirectionDown ? 'down' : 'up');
$sLoadFromURL = utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php?operation=relation_json&relation='.$sRelation.'&direction='.($this->bDirectionDown ? 'down' : 'up'); $sLoadFromURL = utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php?operation=relation_json&relation='.$sRelation.'&direction='.($this->bDirectionDown ? 'down' : 'up');
$sAttachmentExportTitle = ''; $sAttachmentExportTitle = '';
if (($sObjClass != null) && ($iObjKey != null)) if (($sObjClass != null) && ($iObjKey != null)) {
{
$oTargetObj = MetaModel::GetObject($sObjClass, $iObjKey, false); $oTargetObj = MetaModel::GetObject($sObjClass, $iObjKey, false);
if ($oTargetObj) if ($oTargetObj) {
{
$sAttachmentExportTitle = Dict::Format('UI:Relation:AttachmentExportOptions_Name', $oTargetObj->GetName()); $sAttachmentExportTitle = Dict::Format('UI:Relation:AttachmentExportOptions_Name', $oTargetObj->GetName());
} }
} }
$sId = 'graph'; $sId = 'graph';
$sStyle = ''; $sStyle = '';
if ($oP->IsPrintableVersion()) if ($oP->IsPrintableVersion()) {
{
// Optimize for printing on A4/Letter vertically // Optimize for printing on A4/Letter vertically
$sStyle = 'margin-left:auto; margin-right:auto;'; $sStyle = 'margin-left:auto; margin-right:auto;';
$oP->add_ready_script("$('.simple-graph').width(18/2.54*96).resizable({ stop: function() { $(window).trigger('resized'); }});"); // Default width about 18 cm, since most browsers assume 96 dpi $oP->add_ready_script("$('.simple-graph').width(18/2.54*96).resizable({ stop: function() { $(window).trigger('resized'); }});"); // Default width about 18 cm, since most browsers assume 96 dpi
} }
$oP->add('<div id="'.$sId.'" class="simple-graph" style="'.$sStyle.'"></div>'); $oP->add('<div id="'.$sId.'" class="simple-graph" style="'.$sStyle.'"></div>');
$aParams = array( $aParams = array(
'source_url' => $sLoadFromURL, 'source_url' => $sLoadFromURL,
'sources' => ($this->bDirectionDown ? $this->aSourceObjects : $this->aSinkObjects), 'sources' => ($this->bDirectionDown ? $this->aSourceObjects : $this->aSinkObjects),
'excluded' => $aExcludedByClass, 'excluded' => $aExcludedByClass,
'grouping_threshold' => $iGroupingThreshold, 'grouping_threshold' => $iGroupingThreshold,
'export_as_pdf' => array('url' => $sExportAsPdfURL, 'label' => Dict::S('UI:Relation:ExportAsPDF')), 'export_as_pdf' => array('url' => $sExportAsPdfURL, 'label' => Dict::S('UI:Relation:ExportAsPDF')),
'export_as_attachment' => array('url' => $sExportAsDocumentURL, 'label' => Dict::S('UI:Relation:ExportAsAttachment'), 'obj_class' => $sObjClass, 'obj_key' => $iObjKey), 'export_as_attachment' => array('url' => $sExportAsDocumentURL, 'label' => Dict::S('UI:Relation:ExportAsAttachment'), 'obj_class' => $sObjClass, 'obj_key' => $iObjKey),
'drill_down' => array('url' => $sDrillDownURL, 'label' => Dict::S('UI:Relation:DrillDown')), 'drill_down' => array('url' => $sDrillDownURL, 'label' => Dict::S('UI:Relation:DrillDown')),
'labels' => array( 'labels' => array(
'export_pdf_title' => Dict::S('UI:Relation:PDFExportOptions'), 'export_pdf_title' => Dict::S('UI:Relation:PDFExportOptions'),
'export_as_attachment_title' => $sAttachmentExportTitle, 'export_as_attachment_title' => $sAttachmentExportTitle,
'export' => Dict::S('UI:Button:Export'), 'export' => Dict::S('UI:Button:Export'),
'cancel' => Dict::S('UI:Button:Cancel'), 'cancel' => Dict::S('UI:Button:Cancel'),
'title' => Dict::S('UI:RelationOption:Title'), 'title' => Dict::S('UI:RelationOption:Title'),
'untitled' => Dict::S('UI:RelationOption:Untitled'), 'untitled' => Dict::S('UI:RelationOption:Untitled'),
'include_list' => Dict::S('UI:RelationOption:IncludeList'), 'include_list' => Dict::S('UI:RelationOption:IncludeList'),
'comments' => Dict::S('UI:RelationOption:Comments'), 'comments' => Dict::S('UI:RelationOption:Comments'),
'grouping_threshold' => Dict::S('UI:RelationOption:GroupingThreshold'), 'grouping_threshold' => Dict::S('UI:RelationOption:GroupingThreshold'),
'refresh' => Dict::S('UI:Button:Refresh'), 'refresh' => Dict::S('UI:Button:Refresh'),
'check_all' => Dict::S('UI:SearchValue:CheckAll'), 'check_all' => Dict::S('UI:SearchValue:CheckAll'),
'uncheck_all' => Dict::S('UI:SearchValue:UncheckAll'), 'uncheck_all' => Dict::S('UI:SearchValue:UncheckAll'),
'none_selected' => Dict::S('UI:Relation:NoneSelected'), 'none_selected' => Dict::S('UI:Relation:NoneSelected'),
'nb_selected' => Dict::S('UI:SearchValue:NbSelected'), 'nb_selected' => Dict::S('UI:SearchValue:NbSelected'),
'additional_context_info' => Dict::S('UI:Relation:AdditionalContextInfo'), 'additional_context_info' => Dict::S('UI:Relation:AdditionalContextInfo'),
'zoom' => Dict::S('UI:Relation:Zoom'), 'zoom' => Dict::S('UI:Relation:Zoom'),
'loading' => Dict::S('UI:Loading'), 'loading' => Dict::S('UI:Loading'),
), ),
'page_format' => array( 'page_format' => array(
'label' => Dict::S('UI:Relation:PDFExportPageFormat'), 'label' => Dict::S('UI:Relation:PDFExportPageFormat'),
'values' => array( 'values' => array(
'A3' => Dict::S('UI:PageFormat_A3'), 'A3' => Dict::S('UI:PageFormat_A3'),
'A4' => Dict::S('UI:PageFormat_A4'), 'A4' => Dict::S('UI:PageFormat_A4'),
'Letter' => Dict::S('UI:PageFormat_Letter'), 'Letter' => Dict::S('UI:PageFormat_Letter'),
), ),
), ),
'page_orientation' => array( 'page_orientation' => array(
'label' => Dict::S('UI:Relation:PDFExportPageOrientation'), 'label' => Dict::S('UI:Relation:PDFExportPageOrientation'),
'values' => array( 'values' => array(
'P' => Dict::S('UI:PageOrientation_Portrait'), 'P' => Dict::S('UI:PageOrientation_Portrait'),
'L' => Dict::S('UI:PageOrientation_Landscape'), 'L' => Dict::S('UI:PageOrientation_Landscape'),
), ),
), ),
'additional_contexts' => $aAdditionalContexts, 'additional_contexts' => $aAdditionalContexts,
'context_key' => $sContextKey, 'context_key' => $sContextKey,
); );
if (!extension_loaded('gd')) if (!extension_loaded('gd')) {
{
// PDF export requires GD // PDF export requires GD
unset($aParams['export_as_pdf']); unset($aParams['export_as_pdf']);
} }
if (!extension_loaded('gd') || is_null($sObjClass) || is_null($iObjKey)) if (!extension_loaded('gd') || is_null($sObjClass) || is_null($iObjKey)) {
{
// Export as Attachment requires GD (for building the PDF) AND a valid objclass/objkey couple // Export as Attachment requires GD (for building the PDF) AND a valid objclass/objkey couple
unset($aParams['export_as_attachment']); unset($aParams['export_as_attachment']);
} }
$oP->add_ready_script("$('#$sId').simple_graph(".json_encode($aParams).");"); if ($oP->IsPrintableVersion() || !$sLazyLoading) {
$oP->add_ready_script(" $('#$sId').simple_graph(".json_encode($aParams).");");
} else {
$oP->add_script("function Load(){var aExcluded = []; $('input[name^=excluded]').each( function() {if (!$(this).prop('checked')) { aExcluded.push($(this).val()); }} ); var params= $.extend(".json_encode($aParams).", {excluded_classes: aExcluded}); $('#$sId').simple_graph(params);}");
}
} }
catch(Exception $e) catch(Exception $e)
{ {
$oP->add('<div>'.$e->getMessage().'</div>'); $oP->add('<div>'.$e->getMessage().'</div>');
} }
$oP->add_script( $oP->add_script(
<<<EOF <<<EOF
function DoReload() function DoReload()
{ {
@@ -1611,5 +1544,95 @@ EOF
EOF EOF
); );
} }
/**
* @param string $sContextKey
* @param array $aContextParams
* @param array $aExcludedObjects
* @param \WebPage $oP
* @param array $aResults
*
* @return array
* @throws \CoreException
* @throws \DictExceptionMissingString
* @throws \ReflectionException
* @throws \Twig\Error\LoaderError
* @throws \Twig\Error\RuntimeError
* @throws \Twig\Error\SyntaxError
*/
public function DisplayFiltering(string $sContextKey, array $aContextParams, array $aExcludedObjects, WebPage $oP, array $aResults, bool $sLazyLoading = false): array
{
$aContextDefs = static::GetContextDefinitions($sContextKey, true, $aContextParams);
$aExcludedByClass = array();
foreach ($aExcludedObjects as $oObj) {
if (!array_key_exists(get_class($oObj), $aExcludedByClass)) {
$aExcludedByClass[get_class($oObj)] = array();
}
$aExcludedByClass[get_class($oObj)][] = $oObj->GetKey();
}
$sSftShort = Dict::S('UI:ElementsDisplayed');
$oP->add("<div class=\"not-printable\">\n");
$oUiSearchBlock = new Panel($sSftShort, [], Panel::ENUM_COLOR_SCHEME_CYAN, 'dh_flash');
$oUiSearchBlock->SetCSSClasses(["ibo-search-form-panel", "display_block"]);
$oUiSearchBlock->SetIsCollapsible(true);
$oUiHtmlBlock = new Combodo\iTop\Application\UI\Base\Component\Html\Html(
<<<EOF
<div id="ds_flash" class="search_box ibo-display-graph--search-box">
<div id="dh_flash_criterion_outer" class="sf_criterion_area"><div class="sf_criterion_row">
EOF
);
$oP->add_ready_script(
<<<EOF
$("#dh_flash > .sf_title").on("click", function() {
$("#dh_flash").toggleClass("closed");
});
$("#ReloadMovieBtn").button().button("disable");
EOF
);
if ($sLazyLoading) {
$oP->add_ready_script("$('#ReloadMovieBtn').button('enable');");
} else {
$oP->add_ready_script("$('#dh_flash').addClass('closed');");
}
$aSortedElements = array();
foreach ($aResults as $sClassIdx => $aObjects) {
foreach ($aObjects as $oCurrObj) {
$sSubClass = get_class($oCurrObj);
$aSortedElements[$sSubClass] = MetaModel::GetName($sSubClass);
}
}
asort($aSortedElements);
$idx = 0;
foreach ($aSortedElements as $sSubClass => $sClassName) {
$oUiHtmlBlock->AddHtml("<div><input type=\"checkbox\" id=\"exclude_$idx\" name=\"excluded[]\" value=\"$sSubClass\" checked onChange=\"$('#ReloadMovieBtn').button('enable')\"><label for=\"exclude_$idx\">");
$oUiMedallionBlock = new MedallionIcon(MetaModel::GetClassIcon($sSubClass, false));
$oUiMedallionBlock->SetDescription($sClassName);
$oUiHtmlBlock->AddHtml(BlockRenderer::RenderBlockTemplates($oUiMedallionBlock));
$oUiHtmlBlock->AddHtml("</label></div>");
$idx++;
}
$oUiHtmlBlock->AddHtml("</div>");
if ($sLazyLoading) {
$sOnCLick = "Load(); $('#ReloadMovieBtn').attr('onclick','DoReload()');$('#ReloadMovieBtn').html('".Dict::S('UI:Button:Refresh')."');";
$oUiHtmlBlock->AddHtml("<button type=\"button\" id=\"ReloadMovieBtn\" class=\"ibo-button ibo-is-neutral ibo-is-regular\" onClick=\"$sOnCLick\">".Dict::S('Relation:impacts/LoadData')."</button></div></form>");
} else {
$sOnCLick = "DoReload()";
$oUiHtmlBlock->AddHtml("<button type=\"button\" id=\"ReloadMovieBtn\" class=\"ibo-button ibo-is-neutral ibo-is-regular\" onClick=\"$sOnCLick\">".Dict::S('UI:Button:Refresh')."</button></div></form>");
}
$oUiHtmlBlock->AddHtml("</div>\n");
$oUiHtmlBlock->AddHtml("</div>\n"); // class="not-printable"
$oUiSearchBlock->AddSubBlock($oUiHtmlBlock);
$oP->AddUiBlock($oUiSearchBlock);
$aAdditionalContexts = array();
foreach ($aContextDefs as $sKey => $aDefinition) {
$aAdditionalContexts[] = array('key' => $sKey, 'label' => Dict::S($aDefinition['dict']), 'oql' => $aDefinition['oql'], 'default' => (array_key_exists('default', $aDefinition) && ($aDefinition['default'] == 'yes')));
}
return array($aExcludedByClass, $aAdditionalContexts);
}
} }

File diff suppressed because it is too large Load Diff

View File

@@ -6,6 +6,7 @@
use Combodo\iTop\Application\Helper\Session; use Combodo\iTop\Application\Helper\Session;
use Combodo\iTop\Application\TwigBase\Twig\TwigHelper; use Combodo\iTop\Application\TwigBase\Twig\TwigHelper;
use Combodo\iTop\Application\UI\Base\Component\Alert\AlertUIBlockFactory;
use Combodo\iTop\Application\UI\Base\Component\Button\ButtonUIBlockFactory; use Combodo\iTop\Application\UI\Base\Component\Button\ButtonUIBlockFactory;
use Combodo\iTop\Application\UI\Base\Component\DataTable\DataTableUIBlockFactory; use Combodo\iTop\Application\UI\Base\Component\DataTable\DataTableUIBlockFactory;
use Combodo\iTop\Application\UI\Base\Component\Form\Form; use Combodo\iTop\Application\UI\Base\Component\Form\Form;
@@ -18,6 +19,7 @@ use Combodo\iTop\Application\UI\Base\Component\Title\TitleUIBlockFactory;
use Combodo\iTop\Application\UI\Base\Component\Toolbar\ToolbarUIBlockFactory; use Combodo\iTop\Application\UI\Base\Component\Toolbar\ToolbarUIBlockFactory;
use Combodo\iTop\Application\UI\Base\Layout\PageContent\PageContentFactory; use Combodo\iTop\Application\UI\Base\Layout\PageContent\PageContentFactory;
use Combodo\iTop\Application\UI\Base\Layout\UIContentBlock; use Combodo\iTop\Application\UI\Base\Layout\UIContentBlock;
use Combodo\iTop\Application\UI\Base\Layout\UIContentBlockUIBlockFactory;
/** /**
* Displays a popup welcome message, once per session at maximum * Displays a popup welcome message, once per session at maximum
@@ -266,21 +268,12 @@ function DisplayMultipleSelectionForm(WebPage $oP, DBSearch $oFilter, string $sN
function DisplayNavigatorListTab($oP, $aResults, $sRelation, $sDirection, $oObj) function DisplayNavigatorListTab($oP, $aResults, $sRelation, $sDirection, $oObj)
{ {
$oP->SetCurrentTab('UI:RelationshipList'); $oP->SetCurrentTab('UI:RelationshipList');
$oP->add("<div id=\"impacted_objects\">"); $oImpactedObject = UIContentBlockUIBlockFactory::MakeStandard("impacted_objects", ['ibo-is-visible']);
$sOldRelation = $sRelation; $oP->AddSubBlock($oImpactedObject);
if (($sRelation == 'impacts') && ($sDirection == 'up')) $oImpactedObject->AddSubBlock(AlertUIBlockFactory::MakeForWarning(Dict::S("Relation:impacts/FilteredData"), '', "alert_filtered_list")->SetIsHidden(true));
{ $oImpactedObjectList = UIContentBlockUIBlockFactory::MakeStandard("impacted_objects_lists", ['ibo-is-visible']);
$sOldRelation = 'depends on'; $oImpactedObject->AddSubBlock($oImpactedObjectList);
} $oImpactedObjectList->AddSubBlock(UIContentBlockUIBlockFactory::MakeStandard("impacted_objects_lists_placeholder", ['ibo-is-visible']));
$oP->add("<div id=\"impacted_objects_lists\">");
$oP->add("<div id=\"impacted_objects_lists_placeholder\"></div>");
/*
* Content is rendered asynchronously via pages/ajax.render.php?operation=relation_lists
*/
$oP->add("</div>");
$oP->add("</div>");
} }
function DisplayNavigatorGroupTab($oP) function DisplayNavigatorGroupTab($oP)