From d9fcd8337074706abae43140dead75ee73964495 Mon Sep 17 00:00:00 2001 From: Denis Flaven Date: Tue, 26 May 2015 16:39:51 +0000 Subject: [PATCH] Impact analysis diagram is now considered as beta ! SVN:trunk[3588] --- core/displayablegraph.class.inc.php | 66 ++++---- .../datamodel.itop-change-mgmt-itil.xml | 144 +++++++++++------- .../datamodel.itop-change-mgmt.xml | 144 +++++++++++------- .../datamodel.itop-incident-mgmt-itil.xml | 38 ++++- .../datamodel.itop-request-mgmt.xml | 25 ++- dictionaries/de.dictionary.itop.ui.php | 3 + dictionaries/dictionary.itop.ui.php | 5 +- dictionaries/fr.dictionary.itop.ui.php | 3 + js/simple_graph.js | 7 + pages/UI.php | 3 +- pages/ajax.render.php | 2 +- 11 files changed, 289 insertions(+), 151 deletions(-) diff --git a/core/displayablegraph.class.inc.php b/core/displayablegraph.class.inc.php index 4bdf7ae99..ed5849c80 100644 --- a/core/displayablegraph.class.inc.php +++ b/core/displayablegraph.class.inc.php @@ -235,8 +235,6 @@ class DisplayableNode extends GraphNode */ public function GroupSimilarNeighbours(DisplayableGraph $oGraph, $iThresholdCount, $bDirectionUp = false, $bDirectionDown = true) { -//echo "

".$this->GetProperty('label').":

"; - if ($this->GetProperty('grouped') === true) return; $this->SetProperty('grouped', true); @@ -270,9 +268,7 @@ class DisplayableNode extends GraphNode { $aNodesPerClass[$sClass][$sKey]['nodes'][$oNode->GetId()] = $oNode; $aNodesPerClass[$sClass][$sKey]['count'] += (int)$oNode->GetProperty('count', 1); -//echo "

New count: ".$aNodesPerClass[$sClass][$sKey]['count']."

"; } - } else { @@ -284,16 +280,14 @@ class DisplayableNode extends GraphNode { foreach($aDefs as $sStatus => $aGroupProps) { -//echo "

$sClass/$sStatus: {$aGroupProps['count']} object(s), actually: ".count($aGroupProps['nodes'])."

"; if (count($aGroupProps['nodes']) >= $iThresholdCount) { - $oNewNode = new DisplayableGroupNode($oGraph, $this->GetId().'::'.$sClass); + $oNewNode = new DisplayableGroupNode($oGraph, $this->GetId().'::'.(($sStatus == 'reached') ? '_reached': '')); $oNewNode->SetProperty('label', 'x'.$aGroupProps['count']); $oNewNode->SetProperty('icon_url', $aGroupProps['icon_url']); $oNewNode->SetProperty('class', $sClass); $oNewNode->SetProperty('is_reached', ($sStatus == 'reached')); $oNewNode->SetProperty('count', $aGroupProps['count']); - //$oNewNode->SetProperty('grouped', true); $oIncomingEdge = new DisplayableEdge($oGraph, $this->GetId().'-'.$oNewNode->GetId(), $this, $oNewNode); @@ -349,7 +343,6 @@ class DisplayableNode extends GraphNode { foreach($aContextRootCauses as $key => $aObjects) { - //$sHtml .= print_r($aContextDefs, true); $aContext = $aContextDefs[$key]; $aRootCauses = array(); foreach($aObjects as $oRootCause) @@ -458,7 +451,6 @@ class DisplayableRedundancyNode extends DisplayableNode { foreach($aDefs as $sStatus => $aNodes) { -//echo "

".$this->GetId().' has '.count($aNodes)." neighbours of class $sClass in status $sStatus\n"; if (count($aNodes) >= $iThresholdCount) { $oNewNode = new DisplayableGroupNode($oGraph, '-'.$this->GetId().'::'.$sClass.'/'.$sStatus); @@ -482,7 +474,6 @@ class DisplayableRedundancyNode extends DisplayableNode $oNewEdge = new DisplayableEdge($oGraph, '-'.$oEdge->GetId().'::'.$sClass.'/'.$sStatus, $oNewNode, $oEdge->GetSinkNode()); } } -//echo "

Replacing ".$oNode->GetId().' by '.$oNewNode->GetId()."\n"; $oGraph->_RemoveNode($oNode); $oNewNode->AddObject($oNode->GetProperty('object')); } @@ -503,10 +494,10 @@ class DisplayableRedundancyNode extends DisplayableNode public function GetTooltip($aContextDefs) { $sHtml = ''; - $sHtml .= "Redundancy


"; + $sHtml .= Dict::S('UI:RelationTooltip:Redundancy')."
"; $sHtml .= ''; - $sHtml .= ""; - $sHtml .= ""; + $sHtml .= ""; + $sHtml .= ""; $sHtml .= '
# Items Impacted: ".$this->GetProperty('is_reached_count')." / ".($this->GetProperty('min_up') + $this->GetProperty('threshold'))."
Critical Threshold: ".$this->GetProperty('threshold')." / ".($this->GetProperty('min_up') + $this->GetProperty('threshold'))."
".Dict::Format('UI:RelationTooltip:ImpactedItems_N_of_M' , $this->GetProperty('is_reached_count'), $this->GetProperty('min_up') + $this->GetProperty('threshold'))."
".Dict::Format('UI:RelationTooltip:CriticalThreshold_N_of_M' , $this->GetProperty('threshold'), $this->GetProperty('min_up') + $this->GetProperty('threshold'))."
'; return $sHtml; } @@ -839,7 +830,6 @@ class DisplayableGraph extends SimpleGraph $aChunks = explode(";", $sDot); foreach($aChunks as $sChunk) { - //echo "

$sChunk

"; if(preg_match('/"([^"]+)".+pos="([0-9\\.]+),([0-9\\.]+)"/ms', $sChunk, $aMatches)) { $sId = $aMatches[1]; @@ -849,12 +839,6 @@ class DisplayableGraph extends SimpleGraph $oNode = $this->GetNode($sId); $oNode->x = (float)$xPos; $oNode->y = (float)$yPos; - - //echo "

$sId at $xPos,$yPos

"; - } - else - { - //echo "

No match

"; } } } @@ -985,7 +969,7 @@ class DisplayableGraph extends SimpleGraph $fBreakMargin = $oPdf->getBreakMargin(); $oPdf->SetAutoPageBreak(false); - $aRemainingArea = $this->RenderKey($oPdf, $sComments, $xMin, $yMin, $xMax, $yMax); + $aRemainingArea = $this->RenderKey($oPdf, $sComments, $xMin, $yMin, $xMax, $yMax, $aContextDefs); $xMin = $aRemainingArea['xmin']; $xMax = $aRemainingArea['xmax']; $yMin = $aRemainingArea['ymin']; @@ -1033,9 +1017,10 @@ class DisplayableGraph extends SimpleGraph * @param float $yMin * @param float $xMax * @param float $yMax + * @param hash $aContextDefs * @return hash An array ('xmin' => , 'xmax' => ,'ymin' => , 'ymax' => ) of the remaining available area to paint the graph */ - protected function RenderKey(TCPDF $oPdf, $sComments, $xMin, $yMin, $xMax, $yMax) + protected function RenderKey(TCPDF $oPdf, $sComments, $xMin, $yMin, $xMax, $yMax, $aContextDefs) { $fFontSize = 7; // in mm $fIconSize = 6; // in mm @@ -1044,6 +1029,8 @@ class DisplayableGraph extends SimpleGraph $fMaxWidth = max($oPdf->GetStringWidth(Dict::S('UI:Relation:Key')) - $fIconSize, $oPdf->GetStringWidth(Dict::S('UI:Relation:Comments')) - $fIconSize); $aClasses = array(); $aIcons = array(); + $aContexts = array(); + $aContextIcons = array(); $oPdf->SetFont('dejavusans', '', $fFontSize, '', true); foreach($oIterator as $sId => $oNode) { @@ -1060,6 +1047,15 @@ class DisplayableGraph extends SimpleGraph $aIcons[$sClass] = $sIconPath; } } + $aContextRootCauses = $oNode->GetProperty('context_root_causes'); + if (!is_null($aContextRootCauses)) + { + foreach($aContextRootCauses as $key => $aObjects) + { + $aContexts[$key] = Dict::S($aContextDefs[$key]['dict']); + $aContextIcons[$key] = APPROOT.'env-'.utils::GetCurrentEnvironment().'/'.$aContextDefs[$key]['icon']; + } + } } $oPdf->SetXY($xMin + $fPadding, $yMin + $fPadding); $yPos = $yMin + $fPadding; @@ -1073,6 +1069,13 @@ class DisplayableGraph extends SimpleGraph $oPdf->Image($aIcons[$sClass], $xMin+1, $yPos, $fIconSize, $fIconSize); $yPos += $fIconSize + 2*$fPadding; } + foreach($aContexts as $key => $sLabel) + { + $oPdf->SetX($xMin + $fIconSize + $fPadding); + $oPdf->Cell(0, $fIconSize + 2*$fPadding, $sLabel, 0 /* border */, 1 /* ln */); + $oPdf->Image($aContextIcons[$key], $xMin+1+$fIconSize*0.125, $yPos+$fIconSize*0.125, $fIconSize*0.75, $fIconSize*0.75); + $yPos += $fIconSize + 2*$fPadding; + } $oPdf->Rect($xMin, $yMin, $fMaxWidth + $fIconSize + 3*$fPadding, $yMax - $yMin, 'D'); if ($sComments != '') @@ -1093,20 +1096,29 @@ class DisplayableGraph extends SimpleGraph return array('xmin' => $fMaxWidth + $fIconSize + 4*$fPadding, 'xmax' => $xMax, 'ymin' => $yMin, 'ymax' => $yMax); } - //itop-tickets/relation_context/UserRequest/impacts/down /** - * + * Get the context definitions from the parameters / configuration. The format of the "key" string is: + * /relation_context/// + * The values will be retrieved for the given class and all its parents and merged together as a single array. + * Entries with an invalid query are removed from the list. * @param string $sContextKey The key to fetch the queries in the configuration. Example: itop-tickets/relation_context/UserRequest/impacts/down + * @param bool $bDevelopParams Whether or not to substitute the parameters inside the queries with the supplied "context params" + * @param array $aContextParams Arguments for the queries (via ToArgs()) if $bDevelopParams == true + * @return multitype:multitype:string */ public function GetContextDefinitions($sContextKey, $bDevelopParams = true, $aContextParams = array()) { $aLevels = explode('/', $sContextKey); + $sLeafClass = $aLevels[2]; + $aRelationContext = MetaModel::GetConfig()->GetModuleSetting($aLevels[0], $aLevels[1], array()); $aContextDefs = array(); - if (isset($aRelationContext[$aLevels[2]][$aLevels[3]][$aLevels[4]]['items'])) + foreach(MetaModel::EnumParentClasses($sLeafClass, ENUM_PARENT_CLASSES_ALL) as $sClass) { - $aContextDefs = $aRelationContext[$aLevels[2]][$aLevels[3]][$aLevels[4]]['items']; - + if (isset($aRelationContext[$sClass][$aLevels[3]][$aLevels[4]]['items'])) + { + $aContextDefs = array_merge($aContextDefs, $aRelationContext[$sClass][$aLevels[3]][$aLevels[4]]['items']); + } } // Check if the queries are valid 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 ec86df4a9..1dde6f534 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 @@ -4518,62 +4518,98 @@ - - - - - - - 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 - - - - - - + + + + + + + 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 + + + + + + + + + + + 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 + + + + + + + + + + + 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 - - - - - - + + + + + + + + Tickets:Related:OpenChanges + itop-change-mgmt/images/change-ongoing.png + + + + 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 + + + + + + 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 d845533f9..1f3287481 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 @@ -819,62 +819,98 @@ - - - - - - - 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 - - - - - - + + + + + + + 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 + + + + + + + + + + + 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 + + + + + + + + + + + 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 - - - - - - + + + + + + + + Tickets:Related:OpenChanges + itop-change-mgmt/images/change-ongoing.png + + + + 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 + + + + + + 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 e323aae8a..6828637b8 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 @@ -1764,6 +1764,19 @@ + + + + + + id)]]> + Tickets:Related:OpenIncidents + itop-request-mgmt/images/incident-red.png + + + + + @@ -1777,11 +1790,7 @@ - - - - - + @@ -1792,10 +1801,27 @@ + + + + + + + + + + + + + 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/datamodel.itop-request-mgmt.xml b/datamodels/2.x/itop-request-mgmt/datamodel.itop-request-mgmt.xml index 2df925ca0..aad41ac3a 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 @@ -1860,11 +1860,7 @@ - - - - - + @@ -1875,10 +1871,27 @@ + + + + + + + + + + + + + Tickets:Related:OpenIncidents + itop-request-mgmt/images/incident-red.png + + + - id)]]> + Tickets:Related:OpenIncidents itop-request-mgmt/images/incident-red.png diff --git a/dictionaries/de.dictionary.itop.ui.php b/dictionaries/de.dictionary.itop.ui.php index 89c19f113..9f03599f2 100644 --- a/dictionaries/de.dictionary.itop.ui.php +++ b/dictionaries/de.dictionary.itop.ui.php @@ -798,6 +798,9 @@ Wenn Aktionen mit Trigger verknüpft sind, bekommt jede Aktion eine Auftragsnumm 'UI:Relation:PDFExportPageOrientation' => 'Page orientation~~', 'UI:PageOrientation_Portrait' => 'Portrait~~', 'UI:PageOrientation_Landscape' => 'Landscape~~', + 'UI:RelationTooltip:Redundancy' => 'Redundancy~~', + 'UI:RelationTooltip:ImpactedItems_N_of_M' => '# of impacted items: %1$d / %2$d~~', + 'UI:RelationTooltip:CriticalThreshold_N_of_M' => 'Critical threshold: %1$d / %2$d~~', 'UI:OperationCancelled' => 'Operation abgebrochen', 'UI:ElementsDisplayed' => 'Filtere', 'Portal:Title' => 'iTop-Benutzerportal', diff --git a/dictionaries/dictionary.itop.ui.php b/dictionaries/dictionary.itop.ui.php index 93d04ace8..97e8ce3d0 100644 --- a/dictionaries/dictionary.itop.ui.php +++ b/dictionaries/dictionary.itop.ui.php @@ -990,7 +990,10 @@ When associated with a trigger, each action is given an "order" number, specifyi 'UI:PageFormat_Letter' => 'Letter', 'UI:Relation:PDFExportPageOrientation' => 'Page orientation', 'UI:PageOrientation_Portrait' => 'Portrait', - 'UI:PageOrientation_Landscape' => 'Landscape', + 'UI:PageOrientation_Landscape' => 'Landscape', + 'UI:RelationTooltip:Redundancy' => 'Redundancy', + 'UI:RelationTooltip:ImpactedItems_N_of_M' => '# of impacted items: %1$d / %2$d', + 'UI:RelationTooltip:CriticalThreshold_N_of_M' => 'Critical threshold: %1$d / %2$d', 'Portal:Title' => 'iTop user portal', 'Portal:NoRequestMgmt' => 'Dear %1$s, you have been redirected to this page because your account is configured with the profile \'Portal user\'. Unfortunately, iTop has not been installed with the feature \'Request Management\'. Please contact your administrator.', 'Portal:Refresh' => 'Refresh', diff --git a/dictionaries/fr.dictionary.itop.ui.php b/dictionaries/fr.dictionary.itop.ui.php index e9e048748..c42808792 100644 --- a/dictionaries/fr.dictionary.itop.ui.php +++ b/dictionaries/fr.dictionary.itop.ui.php @@ -834,6 +834,9 @@ Lors de l\'association à un déclencheur, on attribue à chaque action un numé 'UI:Relation:PDFExportPageOrientation' => 'Orientation de la page', 'UI:PageOrientation_Portrait' => 'Portrait', 'UI:PageOrientation_Landscape' => 'Paysage', + 'UI:RelationTooltip:Redundancy' => 'Redondance', + 'UI:RelationTooltip:ImpactedItems_N_of_M' => 'Nb éléments impactés: %1$d / %2$d', + 'UI:RelationTooltip:CriticalThreshold_N_of_M' => 'Seuil critique: %1$d / %2$d', 'UI:OperationCancelled' => 'Opération Annulée', 'Portal:Title' => 'Portail utilisateur iTop', 'Portal:NoRequestMgmt' => 'Chèr(e) %1$s, vous avez été redirigé(e) vers cette page car votre compte utilisateur est configuré avec le profil \'Utilisateur du Portail\'. Malheureusement, iTop n\'a pas été installé avec le module de \'Gestion des Demandes\'. Merci de contacter votre administrateur iTop.', diff --git a/js/simple_graph.js b/js/simple_graph.js index 4602902ef..0b2ae4f3a 100644 --- a/js/simple_graph.js +++ b/js/simple_graph.js @@ -448,6 +448,7 @@ $(function() var sType = trigger.attr('data-type'); var sNodeId = trigger.attr('data-id'); var oNode = me._find_node(sNodeId); + clearTimeout(trigger.data('openTimeoutId')); /* var sObjName = trigger.attr('data-class'); @@ -643,6 +644,12 @@ $(function() oParams.p = jForm.find(':input[name="p"]').val(); oParams.obj_class = this.options.export_as_attachment.obj_class; oParams.obj_key = this.options.export_as_attachment.obj_key; + oParams.contexts = []; + var me = this; + $('#'+this.element.attr('id')+'_contexts').multiselect('getChecked').each(function() { + oParams.contexts[$(this).val()] = me.options.additional_contexts[$(this).val()].oql; + }); + oParams.context_key = this.options.context_key; var sUrl = jForm.attr('action'); var sTitle = oParams.title; var jPanel = $('#attachments').closest('.ui-tabs-panel'); diff --git a/pages/UI.php b/pages/UI.php index 18f88120e..cc792bfd2 100644 --- a/pages/UI.php +++ b/pages/UI.php @@ -1439,7 +1439,6 @@ EOF $iGroupingThreshold = utils::ReadParam('g', 5); $oObj = MetaModel::GetObject($sClass, $id); - $sRootClass = MetaModel::GetRootClass($sClass); $iMaxRecursionDepth = MetaModel::GetConfig()->Get('relations_max_depth', 20); $aSourceObjects = array($oObj); if ($sRelation == 'depends on') @@ -1477,7 +1476,7 @@ EOF $oP->SetCurrentTabContainer('Navigator'); $sFirstTab = MetaModel::GetConfig()->Get('impact_analysis_first_tab'); - $sContextKey = "itop-config-mgmt/relation_context/$sRootClass/$sRelation/$sDirection"; + $sContextKey = "itop-config-mgmt/relation_context/$sClass/$sRelation/$sDirection"; // Check if the current object supports Attachments, similar to AttachmentPlugin::IsTargetObject $sClassForAttachment = null; diff --git a/pages/ajax.render.php b/pages/ajax.render.php index aa2f210fc..402b64fb7 100644 --- a/pages/ajax.render.php +++ b/pages/ajax.render.php @@ -1744,7 +1744,7 @@ EOF $bIncludeList = (bool)utils::ReadParam('include_list', false); $sComments = utils::ReadParam('comments', '', false, 'raw_data'); $aContexts = utils::ReadParam('contexts', array(), false, 'raw_data'); - $sContextKey = utils::ReadParam('context_key', array(), false, 'raw_data'); + $sContextKey = utils::ReadParam('context_key', '', false, 'raw_data'); $aPositions = null; if ($sPositions != null) {