소스 검색

Impact analysis improvements:
- Better layout and grouping of the graph
- Made the tooltip for groups helpful

git-svn-id: http://svn.code.sf.net/p/itop/code/trunk@3710 a333f486-631f-4898-b8df-5754b55c2be0

dflaven 9 년 전
부모
커밋
8dcea8f57a

+ 86 - 17
core/displayablegraph.class.inc.php

@@ -226,6 +226,16 @@ class DisplayableNode extends GraphNode
 		}
 	}
 	
+	public function GetObjectCount()
+	{
+		return 1;
+	}
+	
+	public function GetObjectClass()
+	{
+		return is_object($this->GetProperty('object', null)) ? get_class($this->GetProperty('object', null)) : null;
+	}
+	
 	/**
 	 * Group together (as a special kind of nodes) all the similar neighbours of the current node
 	 * @param DisplayableGraph $oGraph
@@ -244,11 +254,10 @@ class DisplayableNode extends GraphNode
 			foreach($this->GetOutgoingEdges() as $oEdge)
 			{
 				$oNode = $oEdge->GetSinkNode();
-				
-				if ($oNode->GetProperty('class') !== null)
+				$sClass = $oNode->GetObjectClass();
+				if ($sClass !== null)
 				{
-					$sClass = $oNode->GetProperty('class');
-					if (($sClass!== null) && (!array_key_exists($sClass, $aNodesPerClass)))
+					if (!array_key_exists($sClass, $aNodesPerClass))
 					{
 						$aNodesPerClass[$sClass] = array(
 							'reached' => array(
@@ -267,7 +276,7 @@ class DisplayableNode extends GraphNode
 					if (!array_key_exists($oNode->GetId(), $aNodesPerClass[$sClass][$sKey]['nodes']))
 					{
 						$aNodesPerClass[$sClass][$sKey]['nodes'][$oNode->GetId()] = $oNode;
-						$aNodesPerClass[$sClass][$sKey]['count'] += (int)$oNode->GetProperty('count', 1);
+						$aNodesPerClass[$sClass][$sKey]['count'] += $oNode->GetObjectCount();
 					}
 				}
 				else
@@ -275,14 +284,13 @@ class DisplayableNode extends GraphNode
 					$oNode->GroupSimilarNeighbours($oGraph, $iThresholdCount, $bDirectionUp, $bDirectionDown);
 				}
 			}
-			
 			foreach($aNodesPerClass as $sClass => $aDefs)
 			{
 				foreach($aDefs as $sStatus => $aGroupProps)
 				{
 					if (count($aGroupProps['nodes']) >= $iThresholdCount)
 					{
-						$sNewId = $this->GetId().'::'.(($sStatus == 'reached') ? '_reached': '');
+						$sNewId = $this->GetId().'::'.$sClass.'/'.(($sStatus == 'reached') ? '_reached': '');
 						$oNewNode = $oGraph->GetNode($sNewId);
 						if ($oNewNode == null)
 						{
@@ -293,10 +301,6 @@ class DisplayableNode extends GraphNode
 							$oNewNode->SetProperty('is_reached', ($sStatus == 'reached'));
 							$oNewNode->SetProperty('count', $aGroupProps['count']);
 						}
-						else
-						{
-							$oNewNode->SetProperty('count', $oNewNode->GetProperty('count')+$aGroupProps['count']);
-						}
 						
 						try
 						{
@@ -338,7 +342,18 @@ class DisplayableNode extends GraphNode
 							if ($oGraph->GetNode($oNode->GetId()))
 							{
 								$oGraph->_RemoveNode($oNode);
-								$oNewNode->AddObject($oNode->GetProperty('object'));
+								if ($oNode instanceof DisplayableGroupNode)
+								{
+									// Copy all the objects of the previous group into the new group
+									foreach($oNode->GetObjects() as $oObj)
+									{
+										$oNewNode->AddObject($oObj);
+									}
+								}
+								else
+								{
+									$oNewNode->AddObject($oNode->GetProperty('object'));
+								}								
 							}
 						}
 						$oNewNode->GroupSimilarNeighbours($oGraph, $iThresholdCount, $bDirectionUp, $bDirectionDown);
@@ -385,6 +400,30 @@ class DisplayableNode extends GraphNode
 		$sHtml .= '</tbody></table>';
 		return $sHtml;		
 	}
+	
+	/**
+	 * Get the description of the node in "dot" language
+	 * Used to generate the positions in the graph, but we'd better use fake label
+	 * just to retain the space used by the node, without compromising the parsing
+	 * of the result which may occur when using the real labels (with possible weird characters in the middle)
+	 */
+	public function GetDotAttributes($bNoLabel = false)
+	{
+		$sDot = '';
+		if ($bNoLabel)
+		{
+			// simulate a fake label with the approximate same size as the true label
+			$sLabel = str_repeat('x',strlen($this->GetProperty('label', $this->GetId())));
+			$sDot = 'label="'.$sLabel.'"';
+		}
+		else
+		{
+			// actual label
+			$sLabel = addslashes($this->GetProperty('label', $this->GetId()));
+			$sDot = 'label="'.$sLabel.'"';
+		}
+		return $sDot;
+	}
 }
 
 class DisplayableRedundancyNode extends DisplayableNode
@@ -455,9 +494,9 @@ class DisplayableRedundancyNode extends DisplayableNode
 			{
 				$oNode = $oEdge->GetSourceNode();
 		
-				if (($oNode->GetProperty('class') !== null) && (!$oNode->GetProperty('is_reached')))
+				if (($oNode->GetObjectClass() !== null) && (!$oNode->GetProperty('is_reached')))
 				{
-					$sClass = $oNode->GetProperty('class');
+					$sClass = $oNode->GetObjectClass();
 					if (!array_key_exists($sClass, $aNodesPerClass))
 					{
 						$aNodesPerClass[$sClass] = array('reached' => array(), 'not_reached' => array());
@@ -524,6 +563,17 @@ class DisplayableRedundancyNode extends DisplayableNode
 		$sHtml .= '</tbody></table>';
 		return $sHtml;		
 	}
+	
+
+	public function GetObjectCount()
+	{
+		return 0;
+	}
+
+	public function GetObjectClass()
+	{
+		return null;
+	}
 }
 
 class DisplayableEdge extends GraphEdge
@@ -579,6 +629,11 @@ class DisplayableGroupNode extends DisplayableNode
 	
 	public function AddObject(DBObject $oObj)
 	{
+		$sPrevClass = $this->GetObjectClass();
+		if (($sPrevClass !== null) && (get_class($oObj) !== $sPrevClass))
+		{
+			throw new Exception("Error: adding an object of class '".get_class($oObj)."' to a group of '$sPrevClass' objects.");
+		}
 		$this->aObjects[$oObj->GetKey()] = $oObj;
 	}
 	
@@ -653,9 +708,24 @@ class DisplayableGroupNode extends DisplayableNode
 	{
 		$sHtml = '';
 		$iGroupIdx = $this->GetProperty('group_index');
-		$sHtml .= Dict::Format('UI:RelationGroupNumber_N', (1+$iGroupIdx));
+		$sHtml .= '<a href="#" onclick="$(\'.itop-simple-graph\').simple_graph(\'show_group\', \'relation_group_'.$iGroupIdx.'\');">'.Dict::Format('UI:RelationGroupNumber_N', (1+$iGroupIdx))."</a>";
+		$sHtml .= '<hr/>';
+		$sHtml .= '<table><tbody><tr>';
+		$sHtml .= '<td style="vertical-align:top;padding-right: 0.5em;"><img src="'.$this->GetProperty('icon_url').'"></td><td style="vertical-align:top">'.MetaModel::GetName($this->GetObjectClass()).'<br/>';
+		$sHtml .= Dict::Format('UI_CountOfObjectsShort', $this->GetObjectCount()).'</td>';
+		$sHtml .= '</tr></tbody></table>';
 		return $sHtml;
 	}
+
+	public function GetObjectCount()
+	{
+		return count($this->aObjects);
+	}
+
+	public function GetObjectClass()
+	{
+		return ($this->GetObjectCount() > 0) ? get_class(reset($this->aObjects)) : null;
+	}
 }
 
 /**
@@ -734,7 +804,6 @@ class DisplayableGraph extends SimpleGraph
 					$oNewGraph->aSinkObjects[$sClass][] = $oObj->GetKey();
 					$oNewNode->SetProperty('sink', true);
 				}
-				$oNewNode->SetProperty('class', $sClass);
 				$oNewNode->SetProperty('object', $oObj);
 				$oNewNode->SetProperty('icon_url', $oObj->GetIcon(false));
 				$oNewNode->SetProperty('label', $oObj->GetRawName());
@@ -1065,7 +1134,7 @@ class DisplayableGraph extends SimpleGraph
 		$oPdf->SetFont('dejavusans', '', $fFontSize, '', true);
 		foreach($oIterator as $sId => $oNode)
 		{
-			if ($sClass = $oNode->GetProperty('class'))
+			if ($sClass = $oNode->GetObjectClass())
 			{
 				if (!array_key_exists($sClass, $aClasses))
 				{

+ 10 - 10
datamodels/2.x/itop-change-mgmt-itil/datamodel.itop-change-mgmt-itil.xml

@@ -4524,12 +4524,12 @@
             <down>
               <items type="array">
                 <item id="open_changes" _delta="define">
-                  <oql><![CDATA[SELECT FCI, C FROM FunctionalCI AS FCI JOIN lnkFunctionalCIToTicket AS L ON L.functionalci_id = FCI.id JOIN Change AS C ON L.ticket_id = C.id WHERE (C.status NOT IN ('closed', 'rejected')) AND (L.impact_code != 'not_impacted') AND (C.id != :this->id)]]></oql>
+                  <oql><![CDATA[SELECT FCI, C FROM FunctionalCI AS FCI JOIN lnkFunctionalCIToTicket AS L ON L.functionalci_id = FCI.id JOIN Change AS C ON L.ticket_id = C.id WHERE (C.outage = 'yes') AND (C.status NOT IN ('closed', 'rejected')) AND (L.impact_code != 'not_impacted') AND (C.id != :this->id)]]></oql>
                   <dict>Tickets:Related:OpenChanges</dict>
                   <icon>itop-change-mgmt-itil/images/change-ongoing.png</icon>
                 </item>
                 <item id="recent_changes" _delta="define">
-                  <oql><![CDATA[SELECT FCI, C FROM FunctionalCI AS FCI JOIN lnkFunctionalCIToTicket AS L ON L.functionalci_id = FCI.id JOIN Change AS C ON L.ticket_id = C.id WHERE (C.status NOT IN ('closed')) AND (L.impact_code != 'not_impacted') AND (C.id != :this->id) AND (DATE_ADD(C.end_date, INTERVAL 3 DAY) < NOW())]]></oql>
+                  <oql><![CDATA[SELECT FCI, C FROM FunctionalCI AS FCI JOIN lnkFunctionalCIToTicket AS L ON L.functionalci_id = FCI.id JOIN Change AS C ON L.ticket_id = C.id WHERE (C.outage = 'yes') AND (C.status NOT IN ('closed')) AND (L.impact_code != 'not_impacted') AND (C.id != :this->id) AND (DATE_ADD(C.end_date, INTERVAL 3 DAY) < NOW())]]></oql>
                   <dict>Tickets:Related:RecentChanges</dict>
                   <icon>itop-change-mgmt-itil/images/change-done.png</icon>
                 </item>
@@ -4542,12 +4542,12 @@
             <down>
               <items type="array">
                 <item id="open_changes" _delta="define">
-                  <oql><![CDATA[SELECT FCI, C FROM FunctionalCI AS FCI JOIN lnkFunctionalCIToTicket AS L ON L.functionalci_id = FCI.id JOIN Change AS C ON L.ticket_id = C.id WHERE (C.status NOT IN ('closed', 'rejected')) AND (L.impact_code != 'not_impacted') AND (C.id != :this->id)]]></oql>
+                  <oql><![CDATA[SELECT FCI, C FROM FunctionalCI AS FCI JOIN lnkFunctionalCIToTicket AS L ON L.functionalci_id = FCI.id JOIN Change AS C ON L.ticket_id = C.id WHERE (C.outage = 'yes') AND (C.status NOT IN ('closed', 'rejected')) AND (L.impact_code != 'not_impacted') AND (C.id != :this->id)]]></oql>
                   <dict>Tickets:Related:OpenChanges</dict>
                   <icon>itop-change-mgmt-itil/images/change-ongoing.png</icon>
                 </item>
                 <item id="recent_changes" _delta="define">
-                  <oql><![CDATA[SELECT FCI, C FROM FunctionalCI AS FCI JOIN lnkFunctionalCIToTicket AS L ON L.functionalci_id = FCI.id JOIN Change AS C ON L.ticket_id = C.id WHERE (C.status NOT IN ('closed')) AND (L.impact_code != 'not_impacted') AND (C.id != :this->id) AND (DATE_ADD(C.end_date, INTERVAL 3 DAY) < NOW())]]></oql>
+                  <oql><![CDATA[SELECT FCI, C FROM FunctionalCI AS FCI JOIN lnkFunctionalCIToTicket AS L ON L.functionalci_id = FCI.id JOIN Change AS C ON L.ticket_id = C.id WHERE (C.outage = 'yes') AND (C.status NOT IN ('closed')) AND (L.impact_code != 'not_impacted') AND (C.id != :this->id) AND (DATE_ADD(C.end_date, INTERVAL 3 DAY) < NOW())]]></oql>
                   <dict>Tickets:Related:RecentChanges</dict>
                   <icon>itop-change-mgmt-itil/images/change-done.png</icon>
                 </item>
@@ -4560,12 +4560,12 @@
             <down>
               <items type="array">
                 <item id="open_changes" _delta="define">
-                  <oql><![CDATA[SELECT FCI, C FROM FunctionalCI AS FCI JOIN lnkFunctionalCIToTicket AS L ON L.functionalci_id = FCI.id JOIN Change AS C ON L.ticket_id = C.id WHERE (C.status NOT IN ('closed', 'rejected')) AND (L.impact_code != 'not_impacted') AND (C.id != :this->id)]]></oql>
+                  <oql><![CDATA[SELECT FCI, C FROM FunctionalCI AS FCI JOIN lnkFunctionalCIToTicket AS L ON L.functionalci_id = FCI.id JOIN Change AS C ON L.ticket_id = C.id WHERE (C.outage = 'yes') AND (C.status NOT IN ('closed', 'rejected')) AND (L.impact_code != 'not_impacted') AND (C.id != :this->id)]]></oql>
                   <dict>Tickets:Related:OpenChanges</dict>
                   <icon>itop-change-mgmt-itil/images/change-ongoing.png</icon>
                 </item>
                 <item id="recent_changes" _delta="define">
-                  <oql><![CDATA[SELECT FCI, C FROM FunctionalCI AS FCI JOIN lnkFunctionalCIToTicket AS L ON L.functionalci_id = FCI.id JOIN Change AS C ON L.ticket_id = C.id WHERE (C.status NOT IN ('closed')) AND (L.impact_code != 'not_impacted') AND (C.id != :this->id) AND (DATE_ADD(C.end_date, INTERVAL 3 DAY) < NOW())]]></oql>
+                  <oql><![CDATA[SELECT FCI, C FROM FunctionalCI AS FCI JOIN lnkFunctionalCIToTicket AS L ON L.functionalci_id = FCI.id JOIN Change AS C ON L.ticket_id = C.id WHERE (C.outage = 'yes') AND (C.status NOT IN ('closed')) AND (L.impact_code != 'not_impacted') AND (C.id != :this->id) AND (DATE_ADD(C.end_date, INTERVAL 3 DAY) < NOW())]]></oql>
                   <dict>Tickets:Related:RecentChanges</dict>
                   <icon>itop-change-mgmt-itil/images/change-done.png</icon>
                 </item>
@@ -4582,12 +4582,12 @@
             <down>
               <items type="array">
                 <item id="open_changes" _delta="define">
-                  <oql><![CDATA[SELECT FCI, C FROM FunctionalCI AS FCI JOIN lnkFunctionalCIToTicket AS L ON L.functionalci_id = FCI.id JOIN Change AS C ON L.ticket_id = C.id WHERE (C.status NOT IN ('closed', 'rejected')) AND (L.impact_code != 'not_impacted')]]></oql>
+                  <oql><![CDATA[SELECT FCI, C FROM FunctionalCI AS FCI JOIN lnkFunctionalCIToTicket AS L ON L.functionalci_id = FCI.id JOIN Change AS C ON L.ticket_id = C.id WHERE (C.outage = 'yes') AND (C.status NOT IN ('closed', 'rejected')) AND (L.impact_code != 'not_impacted')]]></oql>
                   <dict>Tickets:Related:OpenChanges</dict>
                   <icon>itop-change-mgmt-itil/images/change-ongoing.png</icon>
                 </item>
                 <item id="recent_changes" _delta="define">
-                  <oql><![CDATA[SELECT FCI, C FROM FunctionalCI AS FCI JOIN lnkFunctionalCIToTicket AS L ON L.functionalci_id = FCI.id JOIN Change AS C ON L.ticket_id = C.id WHERE (C.status IN ('closed')) AND (L.impact_code != 'not_impacted') AND (DATE_ADD(C.end_date, INTERVAL 3 DAY) < NOW())]]></oql>
+                  <oql><![CDATA[SELECT FCI, C FROM FunctionalCI AS FCI JOIN lnkFunctionalCIToTicket AS L ON L.functionalci_id = FCI.id JOIN Change AS C ON L.ticket_id = C.id WHERE (C.outage = 'yes') AND (C.status IN ('closed')) AND (L.impact_code != 'not_impacted') AND (DATE_ADD(C.end_date, INTERVAL 3 DAY) < NOW())]]></oql>
                   <dict>Tickets:Related:RecentChanges</dict>
                   <icon>itop-change-mgmt-itil/images/change-done.png</icon>
                 </item>
@@ -4596,12 +4596,12 @@
             <up>
               <items type="array">
                 <item id="open_changes" _delta="define">
-                  <oql><![CDATA[SELECT FCI, C FROM FunctionalCI AS FCI JOIN lnkFunctionalCIToTicket AS L ON L.functionalci_id = FCI.id JOIN Change AS C ON L.ticket_id = C.id WHERE (C.status NOT IN ('closed', 'rejected')) AND (L.impact_code != 'not_impacted')]]></oql>
+                  <oql><![CDATA[SELECT FCI, C FROM FunctionalCI AS FCI JOIN lnkFunctionalCIToTicket AS L ON L.functionalci_id = FCI.id JOIN Change AS C ON L.ticket_id = C.id WHERE (C.outage = 'yes') AND (C.status NOT IN ('closed', 'rejected')) AND (L.impact_code != 'not_impacted')]]></oql>
                   <dict>Tickets:Related:OpenChanges</dict>
                   <icon>itop-change-mgmt-itil/images/change-ongoing.png</icon>
                 </item>
                 <item id="recent_changes" _delta="define">
-                  <oql><![CDATA[SELECT FCI, C FROM FunctionalCI AS FCI JOIN lnkFunctionalCIToTicket AS L ON L.functionalci_id = FCI.id JOIN Change AS C ON L.ticket_id = C.id WHERE (C.status IN ('closed')) AND (L.impact_code != 'not_impacted') AND (DATE_ADD(C.end_date, INTERVAL 3 DAY) < NOW())]]></oql>
+                  <oql><![CDATA[SELECT FCI, C FROM FunctionalCI AS FCI JOIN lnkFunctionalCIToTicket AS L ON L.functionalci_id = FCI.id JOIN Change AS C ON L.ticket_id = C.id WHERE (C.outage = 'yes') AND (C.status IN ('closed')) AND (L.impact_code != 'not_impacted') AND (DATE_ADD(C.end_date, INTERVAL 3 DAY) < NOW())]]></oql>
                   <dict>Tickets:Related:RecentChanges</dict>
                   <icon>itop-change-mgmt-itil/images/change-done.png</icon>
                 </item>

+ 10 - 10
datamodels/2.x/itop-change-mgmt/datamodel.itop-change-mgmt.xml

@@ -825,12 +825,12 @@
             <down>
               <items type="array">
                 <item id="open_changes" _delta="define">
-                  <oql><![CDATA[SELECT FCI, C FROM FunctionalCI AS FCI JOIN lnkFunctionalCIToTicket AS L ON L.functionalci_id = FCI.id JOIN Change AS C ON L.ticket_id = C.id WHERE (C.status NOT IN ('closed', 'rejected')) AND (L.impact_code != 'not_impacted') AND (C.id != :this->id)]]></oql>
+                  <oql><![CDATA[SELECT FCI, C FROM FunctionalCI AS FCI JOIN lnkFunctionalCIToTicket AS L ON L.functionalci_id = FCI.id JOIN Change AS C ON L.ticket_id = C.id WHERE (C.outage = 'yes') AND (C.status NOT IN ('closed', 'rejected')) AND (L.impact_code != 'not_impacted') AND (C.id != :this->id)]]></oql>
                   <dict>Tickets:Related:OpenChanges</dict>
                   <icon>itop-change-mgmt/images/change-ongoing.png</icon>
                 </item>
                 <item id="recent_changes" _delta="define">
-                  <oql><![CDATA[SELECT FCI, C FROM FunctionalCI AS FCI JOIN lnkFunctionalCIToTicket AS L ON L.functionalci_id = FCI.id JOIN Change AS C ON L.ticket_id = C.id WHERE (C.status NOT IN ('closed')) AND (L.impact_code != 'not_impacted') AND (C.id != :this->id) AND (DATE_ADD(C.end_date, INTERVAL 3 DAY) < NOW())]]></oql>
+                  <oql><![CDATA[SELECT FCI, C FROM FunctionalCI AS FCI JOIN lnkFunctionalCIToTicket AS L ON L.functionalci_id = FCI.id JOIN Change AS C ON L.ticket_id = C.id WHERE (C.outage = 'yes') AND (C.status NOT IN ('closed')) AND (L.impact_code != 'not_impacted') AND (C.id != :this->id) AND (DATE_ADD(C.end_date, INTERVAL 3 DAY) < NOW())]]></oql>
                   <dict>Tickets:Related:RecentChanges</dict>
                   <icon>itop-change-mgmt/images/change-done.png</icon>
                 </item>
@@ -843,12 +843,12 @@
             <down>
               <items type="array">
                 <item id="open_changes" _delta="define">
-                  <oql><![CDATA[SELECT FCI, C FROM FunctionalCI AS FCI JOIN lnkFunctionalCIToTicket AS L ON L.functionalci_id = FCI.id JOIN Change AS C ON L.ticket_id = C.id WHERE (C.status NOT IN ('closed', 'rejected')) AND (L.impact_code != 'not_impacted') AND (C.id != :this->id)]]></oql>
+                  <oql><![CDATA[SELECT FCI, C FROM FunctionalCI AS FCI JOIN lnkFunctionalCIToTicket AS L ON L.functionalci_id = FCI.id JOIN Change AS C ON L.ticket_id = C.id WHERE (C.outage = 'yes') AND (C.status NOT IN ('closed', 'rejected')) AND (L.impact_code != 'not_impacted') AND (C.id != :this->id)]]></oql>
                   <dict>Tickets:Related:OpenChanges</dict>
                   <icon>itop-change-mgmt/images/change-ongoing.png</icon>
                 </item>
                 <item id="recent_changes" _delta="define">
-                  <oql><![CDATA[SELECT FCI, C FROM FunctionalCI AS FCI JOIN lnkFunctionalCIToTicket AS L ON L.functionalci_id = FCI.id JOIN Change AS C ON L.ticket_id = C.id WHERE (C.status NOT IN ('closed')) AND (L.impact_code != 'not_impacted') AND (C.id != :this->id) AND (DATE_ADD(C.end_date, INTERVAL 3 DAY) < NOW())]]></oql>
+                  <oql><![CDATA[SELECT FCI, C FROM FunctionalCI AS FCI JOIN lnkFunctionalCIToTicket AS L ON L.functionalci_id = FCI.id JOIN Change AS C ON L.ticket_id = C.id WHERE (C.outage = 'yes') AND (C.status NOT IN ('closed')) AND (L.impact_code != 'not_impacted') AND (C.id != :this->id) AND (DATE_ADD(C.end_date, INTERVAL 3 DAY) < NOW())]]></oql>
                   <dict>Tickets:Related:RecentChanges</dict>
                   <icon>itop-change-mgmt/images/change-done.png</icon>
                 </item>
@@ -861,12 +861,12 @@
             <down>
               <items type="array">
                 <item id="open_changes" _delta="define">
-                  <oql><![CDATA[SELECT FCI, C FROM FunctionalCI AS FCI JOIN lnkFunctionalCIToTicket AS L ON L.functionalci_id = FCI.id JOIN Change AS C ON L.ticket_id = C.id WHERE (C.status NOT IN ('closed', 'rejected')) AND (L.impact_code != 'not_impacted') AND (C.id != :this->id)]]></oql>
+                  <oql><![CDATA[SELECT FCI, C FROM FunctionalCI AS FCI JOIN lnkFunctionalCIToTicket AS L ON L.functionalci_id = FCI.id JOIN Change AS C ON L.ticket_id = C.id WHERE (C.outage = 'yes') AND (C.status NOT IN ('closed', 'rejected')) AND (L.impact_code != 'not_impacted') AND (C.id != :this->id)]]></oql>
                   <dict>Tickets:Related:OpenChanges</dict>
                   <icon>itop-change-mgmt/images/change-ongoing.png</icon>
                 </item>
                 <item id="recent_changes" _delta="define">
-                  <oql><![CDATA[SELECT FCI, C FROM FunctionalCI AS FCI JOIN lnkFunctionalCIToTicket AS L ON L.functionalci_id = FCI.id JOIN Change AS C ON L.ticket_id = C.id WHERE (C.status NOT IN ('closed')) AND (L.impact_code != 'not_impacted') AND (C.id != :this->id) AND (DATE_ADD(C.end_date, INTERVAL 3 DAY) < NOW())]]></oql>
+                  <oql><![CDATA[SELECT FCI, C FROM FunctionalCI AS FCI JOIN lnkFunctionalCIToTicket AS L ON L.functionalci_id = FCI.id JOIN Change AS C ON L.ticket_id = C.id WHERE (C.outage = 'yes') AND (C.status NOT IN ('closed')) AND (L.impact_code != 'not_impacted') AND (C.id != :this->id) AND (DATE_ADD(C.end_date, INTERVAL 3 DAY) < NOW())]]></oql>
                   <dict>Tickets:Related:RecentChanges</dict>
                   <icon>itop-change-mgmt/images/change-done.png</icon>
                 </item>
@@ -883,12 +883,12 @@
             <down>
               <items type="array">
                 <item id="open_changes" _delta="define">
-                  <oql><![CDATA[SELECT FCI, C FROM FunctionalCI AS FCI JOIN lnkFunctionalCIToTicket AS L ON L.functionalci_id = FCI.id JOIN Change AS C ON L.ticket_id = C.id WHERE (C.status NOT IN ('closed', 'rejected')) AND (L.impact_code != 'not_impacted')]]></oql>
+                  <oql><![CDATA[SELECT FCI, C FROM FunctionalCI AS FCI JOIN lnkFunctionalCIToTicket AS L ON L.functionalci_id = FCI.id JOIN Change AS C ON L.ticket_id = C.id WHERE (C.outage = 'yes') AND (C.status NOT IN ('closed', 'rejected')) AND (L.impact_code != 'not_impacted')]]></oql>
                   <dict>Tickets:Related:OpenChanges</dict>
                   <icon>itop-change-mgmt/images/change-ongoing.png</icon>
                 </item>
                 <item id="recent_changes" _delta="define">
-                  <oql><![CDATA[SELECT FCI, C FROM FunctionalCI AS FCI JOIN lnkFunctionalCIToTicket AS L ON L.functionalci_id = FCI.id JOIN Change AS C ON L.ticket_id = C.id WHERE (C.status IN ('closed')) AND (L.impact_code != 'not_impacted') AND (DATE_ADD(C.end_date, INTERVAL 3 DAY) < NOW())]]></oql>
+                  <oql><![CDATA[SELECT FCI, C FROM FunctionalCI AS FCI JOIN lnkFunctionalCIToTicket AS L ON L.functionalci_id = FCI.id JOIN Change AS C ON L.ticket_id = C.id WHERE (C.outage = 'yes') AND (C.status IN ('closed')) AND (L.impact_code != 'not_impacted') AND (DATE_ADD(C.end_date, INTERVAL 3 DAY) < NOW())]]></oql>
                   <dict>Tickets:Related:RecentChanges</dict>
                   <icon>itop-change-mgmt/images/change-done.png</icon>
                 </item>
@@ -897,12 +897,12 @@
             <up>
               <items type="array">
                 <item id="open_changes" _delta="define">
-                  <oql><![CDATA[SELECT FCI, C FROM FunctionalCI AS FCI JOIN lnkFunctionalCIToTicket AS L ON L.functionalci_id = FCI.id JOIN Change AS C ON L.ticket_id = C.id WHERE (C.status NOT IN ('closed', 'rejected')) AND (L.impact_code != 'not_impacted')]]></oql>
+                  <oql><![CDATA[SELECT FCI, C FROM FunctionalCI AS FCI JOIN lnkFunctionalCIToTicket AS L ON L.functionalci_id = FCI.id JOIN Change AS C ON L.ticket_id = C.id WHERE (C.outage = 'yes') AND (C.status NOT IN ('closed', 'rejected')) AND (L.impact_code != 'not_impacted')]]></oql>
                   <dict>Tickets:Related:OpenChanges</dict>
                   <icon>itop-change-mgmt/images/change-ongoing.png</icon>
                 </item>
                 <item id="recent_changes" _delta="define">
-                  <oql><![CDATA[SELECT FCI, C FROM FunctionalCI AS FCI JOIN lnkFunctionalCIToTicket AS L ON L.functionalci_id = FCI.id JOIN Change AS C ON L.ticket_id = C.id WHERE (C.status IN ('closed')) AND (L.impact_code != 'not_impacted') AND (DATE_ADD(C.end_date, INTERVAL 3 DAY) < NOW())]]></oql>
+                  <oql><![CDATA[SELECT FCI, C FROM FunctionalCI AS FCI JOIN lnkFunctionalCIToTicket AS L ON L.functionalci_id = FCI.id JOIN Change AS C ON L.ticket_id = C.id WHERE (C.outage = 'yes') AND (C.status IN ('closed')) AND (L.impact_code != 'not_impacted') AND (DATE_ADD(C.end_date, INTERVAL 3 DAY) < NOW())]]></oql>
                   <dict>Tickets:Related:RecentChanges</dict>
                   <icon>itop-change-mgmt/images/change-done.png</icon>
                 </item>

+ 1 - 0
js/simple_graph.js

@@ -408,6 +408,7 @@ $(function()
 		},
 		show_group: function(sGroupId)
 		{
+			this._close_all_tooltips();
 			// Activate the 3rd tab
 			this.element.closest('.ui-tabs').tabs("option", "active", 2);
 			// Scroll into view the group