diff --git a/core/displayablegraph.class.inc.php b/core/displayablegraph.class.inc.php
index 19d96b5da..4bdf7ae99 100644
--- a/core/displayablegraph.class.inc.php
+++ b/core/displayablegraph.class.inc.php
@@ -81,7 +81,7 @@ class DisplayableNode extends GraphNode
return sqrt($this->Distance2($oNode));
}
- public function GetForRaphael()
+ public function GetForRaphael($aContextDefs)
{
$aNode = array();
$aNode['shape'] = 'icon';
@@ -97,18 +97,28 @@ class DisplayableNode extends GraphNode
$aNode['id'] = $this->GetId();
$fOpacity = ($this->GetProperty('is_reached') ? 1 : 0.4);
$aNode['icon_attr'] = array('opacity' => $fOpacity);
- $aNode['text_attr'] = array('opacity' => $fOpacity);
+ $aNode['text_attr'] = array('opacity' => $fOpacity);
+ $aNode['tooltip'] = $this->GetTooltip($aContextDefs);
+ $aNode['context_icons'] = array();
+ $aContextRootCauses = $this->GetProperty('context_root_causes');
+ if (!is_null($aContextRootCauses))
+ {
+ foreach($aContextRootCauses as $key => $aObjects)
+ {
+ $aNode['context_icons'][] = utils::GetAbsoluteUrlModulesRoot().$aContextDefs[$key]['icon'];
+ }
+ }
return $aNode;
}
- public function RenderAsPDF(TCPDF $oPdf, DisplayableGraph $oGraph, $fScale)
+ public function RenderAsPDF(TCPDF $oPdf, DisplayableGraph $oGraph, $fScale, $aContextDefs)
{
$Alpha = 1.0;
$oPdf->SetFillColor(200, 200, 200);
$oPdf->setAlpha(1);
$sIconUrl = $this->GetProperty('icon_url');
- $sIconPath = str_replace(utils::GetAbsoluteUrlModulesRoot(), APPROOT.'env-production/', $sIconUrl);
+ $sIconPath = str_replace(utils::GetAbsoluteUrlModulesRoot(), APPROOT.'env-'.utils::GetCurrentEnvironment().'/', $sIconUrl);
if ($this->GetProperty('source'))
{
@@ -134,6 +144,24 @@ class DisplayableNode extends GraphNode
$oPdf->Image($sIconPath, ($this->x - 16)*$fScale, ($this->y - 16)*$fScale, 32*$fScale, 32*$fScale);
+ $aContextRootCauses = $this->GetProperty('context_root_causes');
+ if (!is_null($aContextRootCauses))
+ {
+ $idx = 0;
+ foreach($aContextRootCauses as $key => $aObjects)
+ {
+ $sgn = 2*($idx %2) -1;
+ $coef = floor((1+$idx)/2) * $sgn;
+ $alpha = $coef*pi()/4 - pi()/2;
+ $x = $this->x * $fScale + cos($alpha) * 16*1.25 * $fScale;
+ $y = $this->y * $fScale + sin($alpha) * 16*1.25 * $fScale;
+ $l = 32 * $fScale / 3;
+ $sIconPath = APPROOT.'env-'.utils::GetCurrentEnvironment().'/'.$aContextDefs[$key]['icon'];
+ $oPdf->Image($sIconPath, $x - $l/2, $y - $l/2, $l, $l);
+ $idx++;
+ }
+ }
+
$oPdf->SetFont('dejavusans', '', 24 * $fScale, '', true);
$width = $oPdf->GetStringWidth($this->GetProperty('label'));
$height = $oPdf->GetStringHeight(1000, $this->GetProperty('label'));
@@ -309,6 +337,38 @@ class DisplayableNode extends GraphNode
}
}
}
+
+ public function GetTooltip($aContextDefs)
+ {
+ $sHtml = '';
+ $oCurrObj = $this->GetProperty('object');
+ $sSubClass = get_class($oCurrObj);
+ $sHtml .= $oCurrObj->GetHyperlink()."
";
+ $aContextRootCauses = $this->GetProperty('context_root_causes');
+ if (!is_null($aContextRootCauses))
+ {
+ foreach($aContextRootCauses as $key => $aObjects)
+ {
+ //$sHtml .= print_r($aContextDefs, true);
+ $aContext = $aContextDefs[$key];
+ $aRootCauses = array();
+ foreach($aObjects as $oRootCause)
+ {
+ $aRootCauses[] = $oRootCause->GetHyperlink();
+ }
+ $sHtml .= '
'.implode(', ', $aRootCauses).'
';
+ }
+ $sHtml .= '
';
+ }
+ $sHtml .= '';
+ foreach(MetaModel::GetZListItems($sSubClass, 'list') as $sAttCode)
+ {
+ $oAttDef = MetaModel::GetAttributeDef($sSubClass, $sAttCode);
+ $sHtml .= '| '.$oAttDef->GetLabel().': | '.$oCurrObj->GetAsHtml($sAttCode).' |
';
+ }
+ $sHtml .= '
';
+ return $sHtml;
+ }
}
class DisplayableRedundancyNode extends DisplayableNode
@@ -318,7 +378,7 @@ class DisplayableRedundancyNode extends DisplayableNode
return 24;
}
- public function GetForRaphael()
+ public function GetForRaphael($aContextDefs)
{
$aNode = array();
$aNode['shape'] = 'disc';
@@ -330,16 +390,25 @@ class DisplayableRedundancyNode extends DisplayableNode
$aNode['label'] = $this->GetLabel();
$aNode['id'] = $this->GetId();
$fDiscOpacity = ($this->GetProperty('is_reached') ? 1 : 0.2);
- $aNode['disc_attr'] = array('stroke-width' => 3, 'stroke' => '#000', 'fill' => '#c33', 'opacity' => $fDiscOpacity);
+ $sColor = ($this->GetProperty('is_reached_count') > $this->GetProperty('threshold')) ? '#c33' : '#999';
+ $aNode['disc_attr'] = array('stroke-width' => 3, 'stroke' => '#000', 'fill' => $sColor, 'opacity' => $fDiscOpacity);
$fTextOpacity = ($this->GetProperty('is_reached') ? 1 : 0.4);
- $aNode['text_attr'] = array('fill' => '#fff', 'opacity' => $fTextOpacity);
+ $aNode['text_attr'] = array('fill' => '#fff', 'opacity' => $fTextOpacity);
+ $aNode['tooltip'] = $this->GetTooltip($aContextDefs);
return $aNode;
}
- public function RenderAsPDF(TCPDF $oPdf, DisplayableGraph $oGraph, $fScale)
+ public function RenderAsPDF(TCPDF $oPdf, DisplayableGraph $oGraph, $fScale, $aContextDefs)
{
$oPdf->SetAlpha(1);
- $oPdf->SetFillColor(200, 0, 0);
+ if($this->GetProperty('is_reached_count') > $this->GetProperty('threshold'))
+ {
+ $oPdf->SetFillColor(200, 0, 0);
+ }
+ else
+ {
+ $oPdf->SetFillColor(144, 144, 144);
+ }
$oPdf->SetDrawColor(0, 0, 0);
$oPdf->Circle($this->x*$fScale, $this->y*$fScale, 16*$fScale, 0, 360, 'DF');
@@ -430,11 +499,22 @@ class DisplayableRedundancyNode extends DisplayableNode
}
}
}
+
+ public function GetTooltip($aContextDefs)
+ {
+ $sHtml = '';
+ $sHtml .= "Redundancy
";
+ $sHtml .= '';
+ $sHtml .= "| # Items Impacted: | ".$this->GetProperty('is_reached_count')." / ".($this->GetProperty('min_up') + $this->GetProperty('threshold'))." |
";
+ $sHtml .= "| Critical Threshold: | ".$this->GetProperty('threshold')." / ".($this->GetProperty('min_up') + $this->GetProperty('threshold'))." |
";
+ $sHtml .= '
';
+ return $sHtml;
+ }
}
class DisplayableEdge extends GraphEdge
{
- public function RenderAsPDF(TCPDF $oPdf, DisplayableGraph $oGraph, $fScale)
+ public function RenderAsPDF(TCPDF $oPdf, DisplayableGraph $oGraph, $fScale, $aContextDefs)
{
$xStart = $this->GetSourceNode()->x * $fScale;
$yStart = $this->GetSourceNode()->y * $fScale;
@@ -498,7 +578,7 @@ class DisplayableGroupNode extends DisplayableNode
return 50;
}
- public function GetForRaphael()
+ public function GetForRaphael($aContextDefs)
{
$aNode = array();
$aNode['shape'] = 'group';
@@ -515,10 +595,11 @@ class DisplayableGroupNode extends DisplayableNode
$aNode['icon_attr'] = array('opacity' => $fTextOpacity);
$aNode['disc_attr'] = array('stroke-width' => 3, 'stroke' => '#000', 'fill' => '#fff', 'opacity' => $fDiscOpacity);
$aNode['text_attr'] = array('fill' => '#000', 'opacity' => $fTextOpacity);
+ $aNode['tooltip'] = $this->GetTooltip($aContextDefs);
return $aNode;
}
- public function RenderAsPDF(TCPDF $oPdf, DisplayableGraph $oGraph, $fScale)
+ public function RenderAsPDF(TCPDF $oPdf, DisplayableGraph $oGraph, $fScale, $aContextDefs)
{
$bReached = $this->GetProperty('is_reached');
$oPdf->SetFillColor(255, 255, 255);
@@ -533,7 +614,7 @@ class DisplayableGroupNode extends DisplayableNode
$oPdf->SetLineStyle(array('width' => 2*$fScale, 'cap' => 'round', 'join' => 'miter', 'dash' => 0, 'color' => $aBorderColor));
$sIconUrl = $this->GetProperty('icon_url');
- $sIconPath = str_replace(utils::GetAbsoluteUrlModulesRoot(), APPROOT.'env-production/', $sIconUrl);
+ $sIconPath = str_replace(utils::GetAbsoluteUrlModulesRoot(), APPROOT.'env-'.utils::GetCurrentEnvironment().'/', $sIconUrl);
$oPdf->SetAlpha(1);
$oPdf->Circle($this->x*$fScale, $this->y*$fScale, $this->GetWidth() / 2 * $fScale, 0, 360, 'DF');
@@ -553,6 +634,14 @@ class DisplayableGroupNode extends DisplayableNode
$oPdf->SetTextColor(0, 0, 0);
$oPdf->Text($this->x*$fScale - $width/2, ($this->y + 25)*$fScale, $this->GetProperty('label'));
}
+
+ public function GetTooltip($aContextDefs)
+ {
+ $sHtml = '';
+ $iGroupIdx = $this->GetProperty('group_index');
+ $sHtml .= Dict::Format('UI:RelationGroupNumber_N', (1+$iGroupIdx));
+ return $sHtml;
+ }
}
/**
@@ -633,12 +722,17 @@ class DisplayableGraph extends SimpleGraph
$oNewNode->SetProperty('icon_url', $oObj->GetIcon(false));
$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('developped', $oNode->GetProperty('developped'));
+ $oNewNode->SetProperty('is_reached_allowed', $oNode->GetProperty('is_reached_allowed'));
+ $oNewNode->SetProperty('context_root_causes', $oNode->GetProperty('context_root_causes'));
break;
default:
$oNewNode = new DisplayableRedundancyNode($oNewGraph, $oNode->GetId(), 0, 0);
- $oNewNode->SetProperty('label', $oNode->GetProperty('min_up'));
+ $iNbReached = (is_null($oNode->GetProperty('is_reached_count'))) ? 0 : $oNode->GetProperty('is_reached_count');
+ $oNewNode->SetProperty('label', $iNbReached."/".($oNode->GetProperty('min_up') + $oNode->GetProperty('threshold')));
+ $oNewNode->SetProperty('min_up', $oNode->GetProperty('min_up'));
+ $oNewNode->SetProperty('threshold', $oNode->GetProperty('threshold'));
+ $oNewNode->SetProperty('is_reached_count', $iNbReached);
$oNewNode->SetProperty('is_reached', true);
}
}
@@ -677,16 +771,24 @@ class DisplayableGraph extends SimpleGraph
}
}
- $iNbGrouping = 1;
- //for($iter=0; $iter<$iNbGrouping; $iter++)
+ $oNodesIter = new RelationTypeIterator($oNewGraph, 'Node');
+ foreach($oNodesIter as $oNode)
{
- $oNodesIter = new RelationTypeIterator($oNewGraph, 'Node');
- foreach($oNodesIter as $oNode)
+ if ($oNode->GetProperty('source'))
{
- if ($oNode->GetProperty('source'))
- {
- $oNode->GroupSimilarNeighbours($oNewGraph, $iGroupingThreshold, true, true);
- }
+ $oNode->GroupSimilarNeighbours($oNewGraph, $iGroupingThreshold, true, true);
+ }
+ }
+ // Groups numbering
+ $oIterator = new RelationTypeIterator($oNewGraph, 'Node');
+ $iGroupIdx = 0;
+ foreach($oIterator as $oNode)
+ {
+ if ($oNode instanceof DisplayableGroupNode)
+ {
+ $aGroups[] = $oNode->GetObjects();
+ $oNode->SetProperty('group_index', $iGroupIdx);
+ $iGroupIdx++;
}
}
@@ -811,8 +913,10 @@ class DisplayableGraph extends SimpleGraph
/**
* Renders as JSON string suitable for loading into the simple_graph widget
*/
- function GetAsJSON()
+ function GetAsJSON($sContextKey)
{
+ $aContextDefs = $this->GetContextDefinitions($sContextKey, false);
+
$aData = array('nodes' => array(), 'edges' => array());
$iGroupIdx = 0;
$oIterator = new RelationTypeIterator($this, 'Node');
@@ -824,7 +928,7 @@ class DisplayableGraph extends SimpleGraph
$oNode->SetProperty('group_index', $iGroupIdx);
$iGroupIdx++;
}
- $aData['nodes'][] = $oNode->GetForRaphael();
+ $aData['nodes'][] = $oNode->GetForRaphael($aContextDefs);
}
$oIterator = new RelationTypeIterator($this, 'Edge');
@@ -846,13 +950,15 @@ class DisplayableGraph extends SimpleGraph
* Renders the graph in a PDF document: centered in the current page
* @param PDFPage $oPage The PDFPage representing the PDF document to draw into
* @param string $sComments An optional comment to display next to the graph (HTML entities will be escaped, \n replaced by
)
+ * @param string $sContextKey The key to fetch the queries in the configuration. Example: itop-tickets/relation_context/UserRequest/impacts/down
* @param float $xMin Left coordinate of the bounding box to display the graph
* @param float $xMax Right coordinate of the bounding box to display the graph
* @param float $yMin Top coordinate of the bounding box to display the graph
* @param float $yMax Bottom coordinate of the bounding box to display the graph
*/
- function RenderAsPDF(PDFPage $oPage, $sComments = '', $xMin = -1, $xMax = -1, $yMin = -1, $yMax = -1)
+ function RenderAsPDF(PDFPage $oPage, $sComments = '', $sContextKey, $xMin = -1, $xMax = -1, $yMin = -1, $yMax = -1)
{
+ $aContextDefs = $this->GetContextDefinitions($sContextKey, false); // No need to develop the parameters
$oPdf = $oPage->get_tcpdf();
$aBB = $this->GetBoundingBox();
@@ -904,14 +1010,14 @@ class DisplayableGraph extends SimpleGraph
foreach($oIterator as $sId => $oEdge)
{
set_time_limit($iLoopTimeLimit);
- $oEdge->RenderAsPDF($oPdf, $this, $fScale);
+ $oEdge->RenderAsPDF($oPdf, $this, $fScale, $aContextDefs);
}
$oIterator = new RelationTypeIterator($this, 'Node');
foreach($oIterator as $sId => $oNode)
{
set_time_limit($iLoopTimeLimit);
- $oNode->RenderAsPDF($oPdf, $this, $fScale);
+ $oNode->RenderAsPDF($oPdf, $this, $fScale, $aContextDefs);
}
$oIterator = new RelationTypeIterator($this, 'Node');
$oPdf->SetAutoPageBreak(true, $fBreakMargin);
@@ -950,7 +1056,7 @@ class DisplayableGraph extends SimpleGraph
$fMaxWidth = max($width, $fMaxWidth);
$aClasses[$sClass] = $sClassLabel;
$sIconUrl = $oNode->GetProperty('icon_url');
- $sIconPath = str_replace(utils::GetAbsoluteUrlModulesRoot(), APPROOT.'env-production/', $sIconUrl);
+ $sIconPath = str_replace(utils::GetAbsoluteUrlModulesRoot(), APPROOT.'env-'.utils::GetCurrentEnvironment().'/', $sIconUrl);
$aIcons[$sClass] = $sIconPath;
}
}
@@ -987,6 +1093,40 @@ class DisplayableGraph extends SimpleGraph
return array('xmin' => $fMaxWidth + $fIconSize + 4*$fPadding, 'xmax' => $xMax, 'ymin' => $yMin, 'ymax' => $yMax);
}
+ //itop-tickets/relation_context/UserRequest/impacts/down
+ /**
+ *
+ * @param string $sContextKey The key to fetch the queries in the configuration. Example: itop-tickets/relation_context/UserRequest/impacts/down
+ */
+ public function GetContextDefinitions($sContextKey, $bDevelopParams = true, $aContextParams = array())
+ {
+ $aLevels = explode('/', $sContextKey);
+ $aRelationContext = MetaModel::GetConfig()->GetModuleSetting($aLevels[0], $aLevels[1], array());
+ $aContextDefs = array();
+ if (isset($aRelationContext[$aLevels[2]][$aLevels[3]][$aLevels[4]]['items']))
+ {
+ $aContextDefs = $aRelationContext[$aLevels[2]][$aLevels[3]][$aLevels[4]]['items'];
+
+ }
+
+ // Check if the queries are valid
+ foreach($aContextDefs as $sKey => $sDefs)
+ {
+ $sOQL = $aContextDefs[$sKey]['oql'];
+ try
+ {
+ // Expand the parameters. If anything goes wrong, then the query is considered as invalid and removed from the list
+ $oSearch = DBObjectSearch::FromOQL($sOQL);
+ $aContextDefs[$sKey]['oql'] = $oSearch->ToOQL($bDevelopParams, $aContextParams);
+ }
+ catch(Exception $e)
+ {
+ unset($aContextDefs[$sKey]);
+ }
+ }
+ return $aContextDefs;
+ }
+
/**
* Display the graph inside the given page, with the "filter" drawer above it
* @param WebPage $oP
@@ -995,8 +1135,9 @@ class DisplayableGraph extends SimpleGraph
* @param ApplicationContext $oAppContext
* @param array $aExcludedObjects
*/
- function Display(WebPage $oP, $aResults, $sRelation, ApplicationContext $oAppContext, $aExcludedObjects = array(), $sObjClass = null, $iObjKey = null)
+ function Display(WebPage $oP, $aResults, $sRelation, ApplicationContext $oAppContext, $aExcludedObjects = array(), $sObjClass = null, $iObjKey = null, $sContextKey, $aContextParams = array())
{
+ $aContextDefs = $this->GetContextDefinitions($sContextKey, true, $aContextParams);
$aExcludedByClass = array();
foreach($aExcludedObjects as $oObj)
{
@@ -1026,7 +1167,7 @@ EOF
$aSortedElements[$sSubClass] = MetaModel::GetName($sSubClass);
}
}
-
+
asort($aSortedElements);
$idx = 0;
foreach($aSortedElements as $sSubClass => $sClassName)
@@ -1038,7 +1179,13 @@ EOF
$oP->add("\n");
$oP->add("\n");
$oP->add("".Dict::S('UI:ElementsDisplayed')."
\n");
-
+
+ $aAdditionalContexts = array();
+ foreach($aContextDefs as $sKey => $aDefinition)
+ {
+ $aAdditionalContexts[] = array('key' => $sKey, 'label' => Dict::S($aDefinition['dict']), 'oql' => $aDefinition['oql']);
+ }
+
$sDirection = utils::ReadParam('d', 'horizontal');
$iGroupingThreshold = utils::ReadParam('g', 5);
@@ -1087,6 +1234,11 @@ EOF
'comments' => Dict::S('UI:RelationOption:Comments'),
'grouping_threshold' => Dict::S('UI:RelationOption:GroupingThreshold'),
'refresh' => Dict::S('UI:Button:Refresh'),
+ 'check_all' => Dict::S('UI:SearchValue:CheckAll'),
+ 'uncheck_all' => Dict::S('UI:SearchValue:UncheckAll'),
+ 'none_selected' => Dict::S('UI:Relation:NoneSelected'),
+ 'nb_selected' => Dict::S('UI:SearchValue:NbSelected'),
+ 'additional_context_info' => Dict::S('UI:Relation:AdditionalContextInfo'),
),
'page_format' => array(
'label' => Dict::S('UI:Relation:PDFExportPageFormat'),
@@ -1103,16 +1255,17 @@ EOF
'L' => Dict::S('UI:PageOrientation_Landscape'),
),
),
+ 'additional_contexts' => $aAdditionalContexts,
+ 'context_key' => $sContextKey,
);
- if (!extension_loaded('gd'))
+ if (!extension_loaded('gd'))
{
// PDF export requires GD
unset($aParams['export_as_pdf']);
}
if (!extension_loaded('gd') || is_null($sObjClass) || is_null($iObjKey))
{
- // PDF export requires GD AND a valid objclass/objkey couple
- unset($aParams['export_as_pdf']);
+ // Export as Attachment requires GD (for building the PDF) AND a valid objclass/objkey couple
unset($aParams['export_as_attachment']);
}
$oP->add_ready_script("$('#$sId').simple_graph(".json_encode($aParams).");");
diff --git a/core/metamodel.class.php b/core/metamodel.class.php
index 908b9913f..3b1b1194f 100644
--- a/core/metamodel.class.php
+++ b/core/metamodel.class.php
@@ -1458,13 +1458,17 @@ abstract class MetaModel
*
* @return RelationGraph The graph of all the related objects
*/
- static public function GetRelatedObjectsDown($sRelCode, $aSourceObjects, $iMaxDepth = 99, $bEnableRedundancy = true, $aUnreachable = array())
+ static public function GetRelatedObjectsDown($sRelCode, $aSourceObjects, $iMaxDepth = 99, $bEnableRedundancy = true, $aUnreachable = array(), $aContexts = array())
{
$oGraph = new RelationGraph();
foreach ($aSourceObjects as $oObject)
{
$oGraph->AddSourceObject($oObject);
}
+ foreach($aContexts as $key => $sOQL)
+ {
+ $oGraph->AddContextQuery($key, $sOQL);
+ }
$oGraph->ComputeRelatedObjectsDown($sRelCode, $iMaxDepth, $bEnableRedundancy, $aUnreachable);
return $oGraph;
}
@@ -1479,13 +1483,17 @@ abstract class MetaModel
*
* @return RelationGraph The graph of all the related objects
*/
- static public function GetRelatedObjectsUp($sRelCode, $aSourceObjects, $iMaxDepth = 99, $bEnableRedundancy = true)
+ static public function GetRelatedObjectsUp($sRelCode, $aSourceObjects, $iMaxDepth = 99, $bEnableRedundancy = true, $aContexts = array())
{
$oGraph = new RelationGraph();
foreach ($aSourceObjects as $oObject)
{
$oGraph->AddSinkObject($oObject);
}
+ foreach($aContexts as $key => $sOQL)
+ {
+ $oGraph->AddContextQuery($key, $sOQL);
+ }
$oGraph->ComputeRelatedObjectsUp($sRelCode, $iMaxDepth, $bEnableRedundancy);
return $oGraph;
}
diff --git a/core/relationgraph.class.inc.php b/core/relationgraph.class.inc.php
index 5ed1e0326..cbcd72727 100644
--- a/core/relationgraph.class.inc.php
+++ b/core/relationgraph.class.inc.php
@@ -169,6 +169,7 @@ class RelationGraph extends SimpleGraph
protected $aSourceNodes; // Index of source nodes (for a quicker access)
protected $aSinkNodes; // Index of sink nodes (for a quicker access)
protected $aRedundancySettings; // Cache of user settings
+ protected $aContextSearches; // Context ("knowing that") stored as a hash array 'class' => DBObjectSearch
public function __construct()
{
@@ -176,6 +177,7 @@ class RelationGraph extends SimpleGraph
$this->aSourceNodes = array();
$this->aSinkNodes = array();
$this->aRedundancySettings = array();
+ $this->aContextSearches = array();
}
/**
@@ -197,6 +199,74 @@ class RelationGraph extends SimpleGraph
$oSinkNode->SetProperty('sink', true);
$this->aSinkNodes[$oSinkNode->GetId()] = $oSinkNode;
}
+
+ /**
+ * Add a 'context' OQL query, specifying extra objects to be marked as 'is_reached'
+ * even though they are not part of the sources.
+ * @param string $sOQL The OQL query defining the context objects
+ */
+ public function AddContextQuery($key, $sOQL)
+ {
+ if ($sOQL === '') return;
+
+ $oSearch = DBObjectSearch::FromOQL($sOQL);
+ $aAliases = $oSearch->GetSelectedClasses();
+ if (count($aAliases) < 2 )
+ {
+ IssueLog::Error("Invalid context query '$sOQL'. A context query must contain at least two columns.");
+ throw new Exception("Invalid context query '$sOQL'. A context query must contain at least two columns. Columns: ".implode(', ', $aAliases).'. ');
+ }
+ $aAliasNames = array_keys($aAliases);
+ $sClassAlias = $oSearch->GetClassAlias();
+ $oCondition = new BinaryExpression(new FieldExpression('id', $aAliasNames[0]), '=', new VariableExpression('id'));
+ $oSearch->AddConditionExpression($oCondition);
+
+ $sClass = $oSearch->GetClass();
+ if (!array_key_exists($sClass, $this->aContextSearches))
+ {
+ $this->aContextSearches[$sClass] = array();
+ }
+ $this->aContextSearches[$sClass][] = array('key' => $key, 'search' => $oSearch);
+ }
+
+ /**
+ * Determines if the given DBObject is part of a 'context'
+ * @param DBObject $oObj
+ * @return boolean
+ */
+ public function IsPartOfContext(DBObject $oObj, &$aRootCauses)
+ {
+ $bRet = false;
+ $sFinalClass = get_class($oObj);
+ $aParentClasses = MetaModel::EnumParentClasses($sFinalClass, ENUM_PARENT_CLASSES_ALL);
+
+ foreach($aParentClasses as $sClass)
+ {
+ if (array_key_exists($sClass, $this->aContextSearches))
+ {
+ foreach($this->aContextSearches[$sClass] as $aContextQuery)
+ {
+ $aAliases = $aContextQuery['search']->GetSelectedClasses();
+ $aAliasNames = array_keys($aAliases);
+ $sRootCauseAlias = $aAliasNames[1]; // 1st column (=0) = object, second column = root cause
+ $oSet = new DBObjectSet($aContextQuery['search'], array(), array('id' => $oObj->GetKey()));
+ while($aRow = $oSet->FetchAssoc())
+ {
+ if (!is_null($aRow[$sRootCauseAlias]))
+ {
+ if (!array_key_exists($aContextQuery['key'], $aRootCauses))
+ {
+ $aRootCauses[$aContextQuery['key']] = array();
+ }
+ $aRootCauses[$aContextQuery['key']][] = $aRow[$sRootCauseAlias];
+ $bRet = true;
+ }
+ }
+ }
+ }
+ }
+ return $bRet;
+ }
/**
* Build the graph downstream, and mark the nodes that can be reached from the source node
@@ -220,9 +290,6 @@ class RelationGraph extends SimpleGraph
{
$oNode->SetProperty('is_reached_allowed', false);
}
- else
- {
- }
}
// Determine the reached nodes
@@ -231,6 +298,19 @@ class RelationGraph extends SimpleGraph
$oSourceNode->ReachDown('is_reached', true);
//echo "After reaching from {$oSourceNode->GetId()}
\n".$this->DumpAsHtmlImage()."
\n";
}
+
+ // Mark also the "context" nodes as reached and record the "root causes" for each node
+ $oIterator = new RelationTypeIterator($this, 'Node');
+ foreach($oIterator as $oNode)
+ {
+ $oObj = $oNode->GetProperty('object');
+ $aRootCauses = array();
+ if (!is_null($oObj) && $this->IsPartOfContext($oObj, $aRootCauses))
+ {
+ $oNode->SetProperty('context_root_causes', $aRootCauses);
+ $oNode->ReachDown('is_reached', true);
+ }
+ }
}
/**
@@ -245,6 +325,19 @@ class RelationGraph extends SimpleGraph
$this->AddRelatedObjects($sRelCode, false, $oSinkNode, $iMaxDepth, $bEnableRedundancy);
//echo "After processing of {$oSinkNode->GetId()}
\n".$this->DumpAsHtmlImage()."
\n";
}
+
+ // Mark also the "context" nodes as reached and record the "root causes" for each node
+ $oIterator = new RelationTypeIterator($this, 'Node');
+ foreach($oIterator as $oNode)
+ {
+ $oObj = $oNode->GetProperty('object');
+ $aRootCauses = array();
+ if (!is_null($oObj) && $this->IsPartOfContext($oObj, $aRootCauses))
+ {
+ $oNode->SetProperty('context_root_causes', $aRootCauses);
+ $oNode->ReachDown('is_reached', true);
+ }
+ }
}
diff --git a/datamodels/2.x/itop-change-mgmt-itil/datamodel.itop-change-mgmt-itil.xml b/datamodels/2.x/itop-change-mgmt-itil/datamodel.itop-change-mgmt-itil.xml
index 78e8d0369..ec86df4a9 100755
--- a/datamodels/2.x/itop-change-mgmt-itil/datamodel.itop-change-mgmt-itil.xml
+++ b/datamodels/2.x/itop-change-mgmt-itil/datamodel.itop-change-mgmt-itil.xml
@@ -4516,4 +4516,64 @@
fast
+
+
+
+
+
+
+
+ -
+ id)]]>
+ Tickets:Related:OpenChanges
+ itop-change-mgmt/images/change-ongoing.png
+
+ -
+ id) AND (DATE_ADD(C.end_date, INTERVAL 3 DAY) < NOW())]]>
+ Tickets:Related:RecentChanges
+ itop-change-mgmt/images/change-done.png
+
+
+
+
+
+
+
+
+
+
+
+
+
+ -
+
+ Tickets:Related:OpenChanges
+ itop-change-mgmt/images/change-ongoing.png
+
+ -
+
+ Tickets:Related:RecentChanges
+ itop-change-mgmt/images/change-done.png
+
+
+
+
+
+ -
+ id)]]>
+ Tickets:Related:OpenChanges
+ itop-change-mgmt/images/change-ongoing.png
+
+ -
+ id) AND (DATE_ADD(C.end_date, INTERVAL 3 DAY) < NOW())]]>
+ Tickets:Related:RecentChanges
+ itop-change-mgmt/images/change-done.png
+
+
+
+
+
+
+
+
diff --git a/datamodels/2.x/itop-change-mgmt-itil/images/change-done.png b/datamodels/2.x/itop-change-mgmt-itil/images/change-done.png
new file mode 100755
index 000000000..e27142f66
Binary files /dev/null and b/datamodels/2.x/itop-change-mgmt-itil/images/change-done.png differ
diff --git a/datamodels/2.x/itop-change-mgmt-itil/images/change-ongoing.png b/datamodels/2.x/itop-change-mgmt-itil/images/change-ongoing.png
new file mode 100755
index 000000000..0cc46b863
Binary files /dev/null and b/datamodels/2.x/itop-change-mgmt-itil/images/change-ongoing.png differ
diff --git a/datamodels/2.x/itop-change-mgmt/datamodel.itop-change-mgmt.xml b/datamodels/2.x/itop-change-mgmt/datamodel.itop-change-mgmt.xml
index 48cc23167..d845533f9 100755
--- a/datamodels/2.x/itop-change-mgmt/datamodel.itop-change-mgmt.xml
+++ b/datamodels/2.x/itop-change-mgmt/datamodel.itop-change-mgmt.xml
@@ -817,4 +817,64 @@
fast
+
+
+
+
+
+
+
+ -
+ id)]]>
+ Tickets:Related:OpenChanges
+ itop-change-mgmt/images/change-ongoing.png
+
+ -
+ id) AND (DATE_ADD(C.end_date, INTERVAL 3 DAY) < NOW())]]>
+ Tickets:Related:RecentChanges
+ itop-change-mgmt/images/change-done.png
+
+
+
+
+
+
+
+
+
+
+
+
+
+ -
+
+ Tickets:Related:OpenChanges
+ itop-change-mgmt/images/change-ongoing.png
+
+ -
+
+ Tickets:Related:RecentChanges
+ itop-change-mgmt/images/change-done.png
+
+
+
+
+
+ -
+ id)]]>
+ Tickets:Related:OpenChanges
+ itop-change-mgmt/images/change-ongoing.png
+
+ -
+ id) AND (DATE_ADD(C.end_date, INTERVAL 3 DAY) < NOW())]]>
+ Tickets:Related:RecentChanges
+ itop-change-mgmt/images/change-done.png
+
+
+
+
+
+
+
+
diff --git a/datamodels/2.x/itop-change-mgmt/de.dict.itop-change-mgmt.php b/datamodels/2.x/itop-change-mgmt/de.dict.itop-change-mgmt.php
index 8a4e2d6a9..0d7da474a 100644
--- a/datamodels/2.x/itop-change-mgmt/de.dict.itop-change-mgmt.php
+++ b/datamodels/2.x/itop-change-mgmt/de.dict.itop-change-mgmt.php
@@ -102,6 +102,8 @@ Dict::Add('DE DE', 'German', 'Deutsch', array(
'UI-ChangeManagementOverview-Last-7-days' => 'Zahl der Changes in den letzten sieben Tagen',
'UI-ChangeManagementOverview-ChangeByDomain-last-7-days' => 'Changes der letzten sieben Tage nach Typ',
'UI-ChangeManagementOverview-ChangeByStatus-last-7-days' => 'Changes der letzten sieben Tage nach Status',
+ 'Tickets:Related:OpenChanges' => 'Open changes~~',
+ 'Tickets:Related:RecentChanges' => 'Recent changes~~',
'Class:Change/Attribute:changemanager_email' => 'Change Manager Email',
'Class:Change/Attribute:changemanager_email+' => '',
'Class:Change/Attribute:parent_name' => 'Parent Change ref',
diff --git a/datamodels/2.x/itop-change-mgmt/en.dict.itop-change-mgmt.php b/datamodels/2.x/itop-change-mgmt/en.dict.itop-change-mgmt.php
index b70ed3803..76e5bb7b0 100755
--- a/datamodels/2.x/itop-change-mgmt/en.dict.itop-change-mgmt.php
+++ b/datamodels/2.x/itop-change-mgmt/en.dict.itop-change-mgmt.php
@@ -46,6 +46,8 @@ Dict::Add('EN US', 'English', 'English', array(
'UI-ChangeManagementOverview-Last-7-days' => 'Number of changes for the last 7 days',
'UI-ChangeManagementOverview-ChangeByDomain-last-7-days' => 'Changes by domain for the last 7 days',
'UI-ChangeManagementOverview-ChangeByStatus-last-7-days' => 'Changes by status for the last 7 days',
+ 'Tickets:Related:OpenChanges' => 'Open changes',
+ 'Tickets:Related:RecentChanges' => 'Recent changes',
));
// Dictionnay conventions
diff --git a/datamodels/2.x/itop-change-mgmt/fr.dict.itop-change-mgmt.php b/datamodels/2.x/itop-change-mgmt/fr.dict.itop-change-mgmt.php
index 1f4c75e77..6d5c13d7d 100755
--- a/datamodels/2.x/itop-change-mgmt/fr.dict.itop-change-mgmt.php
+++ b/datamodels/2.x/itop-change-mgmt/fr.dict.itop-change-mgmt.php
@@ -125,8 +125,7 @@ Dict::Add('FR FR', 'French', 'Français', array(
'UI-ChangeManagementOverview-ChangeByDomain-last-7-days' => 'Changements par domaine',
'UI-ChangeManagementOverview-ChangeByStatus-last-7-days' => 'Changements par statut',
'UI:ChangeMgmtMenuOverview:Title' => 'Tableau de bord des changements pour les 7 derniers jours',
-
-
-
+ 'Tickets:Related:OpenChanges' => 'Changements en cours',
+ 'Tickets:Related:RecentChanges' => 'Changements récents',
));
?>
diff --git a/datamodels/2.x/itop-change-mgmt/images/change-done.png b/datamodels/2.x/itop-change-mgmt/images/change-done.png
new file mode 100755
index 000000000..e27142f66
Binary files /dev/null and b/datamodels/2.x/itop-change-mgmt/images/change-done.png differ
diff --git a/datamodels/2.x/itop-change-mgmt/images/change-ongoing.png b/datamodels/2.x/itop-change-mgmt/images/change-ongoing.png
new file mode 100755
index 000000000..0cc46b863
Binary files /dev/null and b/datamodels/2.x/itop-change-mgmt/images/change-ongoing.png differ
diff --git a/datamodels/2.x/itop-incident-mgmt-itil/datamodel.itop-incident-mgmt-itil.xml b/datamodels/2.x/itop-incident-mgmt-itil/datamodel.itop-incident-mgmt-itil.xml
index d768ceac6..e323aae8a 100755
--- a/datamodels/2.x/itop-incident-mgmt-itil/datamodel.itop-incident-mgmt-itil.xml
+++ b/datamodels/2.x/itop-incident-mgmt-itil/datamodel.itop-incident-mgmt-itil.xml
@@ -1761,4 +1761,49 @@
+
+
+
+
+
+
+
+ -
+ id)]]>
+ Tickets:Related:OpenIncidents
+ itop-request-mgmt/images/incident-red.png
+
+
+
+
+
+
+
+
+
+
+
+
+
+ -
+ id)]]>
+ Tickets:Related:OpenIncidents
+ itop-request-mgmt/images/incident-red.png
+
+
+
+
+
+ -
+ id)]]>
+ Tickets:Related:OpenIncidents
+ itop-request-mgmt/images/incident-red.png
+
+
+
+
+
+
+
+
diff --git a/datamodels/2.x/itop-incident-mgmt-itil/images/incident-red.png b/datamodels/2.x/itop-incident-mgmt-itil/images/incident-red.png
new file mode 100755
index 000000000..5aac4ab4b
Binary files /dev/null and b/datamodels/2.x/itop-incident-mgmt-itil/images/incident-red.png differ
diff --git a/datamodels/2.x/itop-request-mgmt/datamodel.itop-request-mgmt.xml b/datamodels/2.x/itop-request-mgmt/datamodel.itop-request-mgmt.xml
index 52241a102..2df925ca0 100755
--- a/datamodels/2.x/itop-request-mgmt/datamodel.itop-request-mgmt.xml
+++ b/datamodels/2.x/itop-request-mgmt/datamodel.itop-request-mgmt.xml
@@ -1844,4 +1844,49 @@
fast
+
+
+
+
+
+
+
+ -
+ id)]]>
+ Tickets:Related:OpenIncidents
+ itop-request-mgmt/images/incident-red.png
+
+
+
+
+
+
+
+
+
+
+
+
+
+ -
+ id)]]>
+ Tickets:Related:OpenIncidents
+ itop-request-mgmt/images/incident-red.png
+
+
+
+
+
+ -
+ id)]]>
+ Tickets:Related:OpenIncidents
+ itop-request-mgmt/images/incident-red.png
+
+
+
+
+
+
+
+
diff --git a/datamodels/2.x/itop-request-mgmt/de.dict.itop-request-mgmt.php b/datamodels/2.x/itop-request-mgmt/de.dict.itop-request-mgmt.php
index 8708341a1..76115dfa8 100644
--- a/datamodels/2.x/itop-request-mgmt/de.dict.itop-request-mgmt.php
+++ b/datamodels/2.x/itop-request-mgmt/de.dict.itop-request-mgmt.php
@@ -268,5 +268,6 @@ Dict::Add('DE DE', 'German', 'Deutsch', array(
'Portal:SelectLanguage' => 'Ändern Sie Ihre Spracheinstellung',
'Portal:LanguageChangedTo_Lang' => 'Spracheinstellung geändert auf: ',
'Portal:ChooseYourFavoriteLanguage' => 'WÄhlen Sie Ihre bevorzugte Sprache',
+ 'Tickets:Related:OpenIncidents' => 'Open incidents~~',
));
?>
diff --git a/datamodels/2.x/itop-request-mgmt/en.dict.itop-request-mgmt.php b/datamodels/2.x/itop-request-mgmt/en.dict.itop-request-mgmt.php
index 3e6db35f4..e66d35365 100755
--- a/datamodels/2.x/itop-request-mgmt/en.dict.itop-request-mgmt.php
+++ b/datamodels/2.x/itop-request-mgmt/en.dict.itop-request-mgmt.php
@@ -58,6 +58,7 @@ Dict::Add('EN US', 'English', 'English', array(
'Menu:UserRequest:MyWorkOrders' => 'Work orders assigned to me',
'Menu:UserRequest:MyWorkOrders+' => 'All work orders assigned to me',
'Class:Problem:KnownProblemList' => 'Known problems',
+ 'Tickets:Related:OpenIncidents' => 'Open incidents',
));
// Dictionnay conventions
diff --git a/datamodels/2.x/itop-request-mgmt/fr.dict.itop-request-mgmt.php b/datamodels/2.x/itop-request-mgmt/fr.dict.itop-request-mgmt.php
index 5e3592f9d..8699783d8 100755
--- a/datamodels/2.x/itop-request-mgmt/fr.dict.itop-request-mgmt.php
+++ b/datamodels/2.x/itop-request-mgmt/fr.dict.itop-request-mgmt.php
@@ -215,6 +215,7 @@ Dict::Add('FR FR', 'French', 'Français', array(
'Class:UserRequest/Stimulus:ev_wait_for_approval' => 'Attendre une approbation',
'Class:UserRequest/Stimulus:ev_wait_for_approval+' => '',
'Class:UserRequest/Error:CannotAssignParentRequestIdToSelf' => 'La Requête parente ne peut pas être assignée à elle même',
+ 'Tickets:Related:OpenIncidents' => 'Incidents en cours',
));
diff --git a/datamodels/2.x/itop-request-mgmt/images/incident-red.png b/datamodels/2.x/itop-request-mgmt/images/incident-red.png
new file mode 100755
index 000000000..5aac4ab4b
Binary files /dev/null and b/datamodels/2.x/itop-request-mgmt/images/incident-red.png differ
diff --git a/dictionaries/de.dictionary.itop.ui.php b/dictionaries/de.dictionary.itop.ui.php
index 6e3e9bdd9..89c19f113 100644
--- a/dictionaries/de.dictionary.itop.ui.php
+++ b/dictionaries/de.dictionary.itop.ui.php
@@ -778,6 +778,8 @@ Wenn Aktionen mit Trigger verknüpft sind, bekommt jede Aktion eine Auftragsnumm
'UI:RelationGroupNumber_N' => 'Gruppe #%1$d~~',
'UI:Relation:ExportAsPDF' => 'Export as PDF...~~',
'UI:RelationOption:GroupingThreshold' => 'Grouping threshold~~',
+ 'UI:Relation:AdditionalContextInfo' => 'Additional context info~~',
+ 'UI:Relation:NoneSelected' => 'Nichts~~',
'UI:Relation:ExportAsDocument' => 'Export as Document...~~',
'UI:Relation:DrillDown' => 'Details...~~',
'UI:Relation:PDFExportOptions' => 'PDF Export Options~~',
diff --git a/dictionaries/dictionary.itop.ui.php b/dictionaries/dictionary.itop.ui.php
index 1be80e823..93d04ace8 100644
--- a/dictionaries/dictionary.itop.ui.php
+++ b/dictionaries/dictionary.itop.ui.php
@@ -971,6 +971,8 @@ When associated with a trigger, each action is given an "order" number, specifyi
'UI:RelationGroupNumber_N' => 'Group #%1$d',
'UI:Relation:ExportAsPDF' => 'Export as PDF...',
'UI:RelationOption:GroupingThreshold' => 'Grouping threshold',
+ 'UI:Relation:AdditionalContextInfo' => 'Additional context info',
+ 'UI:Relation:NoneSelected' => 'None',
'UI:Relation:ExportAsAttachment' => 'Export as Attachment...',
'UI:Relation:DrillDown' => 'Details...',
'UI:Relation:PDFExportOptions' => 'PDF Export Options',
diff --git a/dictionaries/fr.dictionary.itop.ui.php b/dictionaries/fr.dictionary.itop.ui.php
index 4a7d1ac3e..e9e048748 100644
--- a/dictionaries/fr.dictionary.itop.ui.php
+++ b/dictionaries/fr.dictionary.itop.ui.php
@@ -814,6 +814,8 @@ Lors de l\'association à un déclencheur, on attribue à chaque action un numé
'UI:RelationGroupNumber_N' => 'Groupe n°%1$d',
'UI:Relation:ExportAsPDF' => 'Exporter en PDF...',
'UI:RelationOption:GroupingThreshold' => 'Seuil de groupage',
+ 'UI:Relation:AdditionalContextInfo' => 'Infos complémentaires de contexte',
+ 'UI:Relation:NoneSelected' => 'Aucune',
'UI:Relation:ExportAsAttachment' => 'Exporter comme une Pièce Jointe...',
'UI:Relation:DrillDown' => 'Détails...',
'UI:Relation:PDFExportOptions' => 'Options de l\'export en PDF',
diff --git a/js/simple_graph.js b/js/simple_graph.js
index 3f4589d8c..4602902ef 100644
--- a/js/simple_graph.js
+++ b/js/simple_graph.js
@@ -28,14 +28,21 @@ $(function()
include_list: 'Include the list of objects',
comments: 'Comments',
grouping_threshold: 'Grouping Threshold',
- refresh: 'Refresh'
+ additional_context_info: 'Additional Context Info',
+ refresh: 'Refresh',
+ check_all: 'Check All',
+ uncheck_all: 'Uncheck All',
+ none_selected: 'None',
+ nb_selected: '# selected',
},
export_as_document: null,
drill_down: null,
grouping_threshold: 10,
excluded_classes: [],
attachment_obj_class: null,
- attachment_obj_key: null
+ attachment_obj_key: null,
+ additional_contexts: [],
+ context_key: ''
},
// the constructor
@@ -107,6 +114,7 @@ $(function()
this.aEdges[k].aElements = [];
this._draw_edge(this.aEdges[k]);
}
+ this._make_tooltips();
},
_draw_node: function(oNode)
{
@@ -158,6 +166,19 @@ $(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).colorShift('#fff', 1));
}
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 idx = 0;
+ for(var i in oNode.context_icons)
+ {
+ var sgn = 2*(idx % 2) -1; // Suite: -1, 1, -1, 1, -1, 1, -1, etc.
+ var coef = Math.floor((1+idx)/2) * sgn; // Suite: 0, 1, -1, 2, -2, 3, -3, etc.
+ var alpha = coef*Math.PI/4 - Math.PI/2;
+ var x = xPos + Math.cos(alpha) * 1.25*iWidth * this.fZoom / 2;
+ var y = yPos + Math.sin(alpha) * 1.25*iWidth * this.fZoom / 2;
+ var l = iWidth/3 * this.fZoom;
+ oNode.aElements.push(this.oPaper.image(oNode.context_icons[i], x - l/2, y - l/2, l , l).attr(oNode.icon_attr));
+ idx++;
+ }
var oText = this.oPaper.text( xPos, yPos, oNode.label);
oNode.text_attr['font-size'] = iFontSize * this.fZoom;
oText.attr(oNode.text_attr);
@@ -186,7 +207,18 @@ $(function()
{
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) {
+ clearTimeout($(this.node).data('openTimeoutId'));
+ 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);
+ }
+ );
}
},
_move: function(sNodeId, dx, dy, x, y, event)
@@ -367,7 +399,17 @@ $(function()
var sPopupMenuId = 'tk_graph'+this.element.attr('id');
var sHtml = '';
var sId = this.element.attr('id');
- sHtml += this.options.labels.grouping_threshold+' ';
+ sHtml += this.options.labels.grouping_threshold+' ';
+ if (this.options.additional_contexts.length > 0)
+ {
+ sHtml += ' '+this.options.labels.additional_context_info+' '
+ }
+ sHtml += ' ';
sHtml += '