Kaynağa Gözat

Refresh of the "Groups" tab in the impact analysis display, when the whole graph is refreshed.

git-svn-id: http://svn.code.sf.net/p/itop/code/trunk@3711 a333f486-631f-4898-b8df-5754b55c2be0
dflaven 9 yıl önce
ebeveyn
işleme
0694a8ca48
4 değiştirilmiş dosya ile 120 ekleme ve 48 silme
  1. 15 2
      core/displayablegraph.class.inc.php
  2. 73 9
      js/simple_graph.js
  3. 14 37
      pages/UI.php
  4. 18 0
      pages/ajax.render.php

+ 15 - 2
core/displayablegraph.class.inc.php

@@ -1001,14 +1001,27 @@ class DisplayableGraph extends SimpleGraph
 	{
 		$aContextDefs = $this->GetContextDefinitions($sContextKey, false);
 		
-		$aData = array('nodes' => array(), 'edges' => array());
+		$aData = array('nodes' => array(), 'edges' => array(), 'groups' => array());
 		$iGroupIdx = 0;
 		$oIterator = new RelationTypeIterator($this, 'Node');
 		foreach($oIterator as $sId => $oNode)
 		{
 			if ($oNode instanceof DisplayableGroupNode)
 			{
-				$aGroups[] = $oNode->GetObjects();
+				// The contents of the "Groups" tab will be rendered
+				// using a separate ajax call, since the content of
+				// the page is made of a mix of HTML / CSS / JS which
+				// cannot be conveyed easily in the JSON structure
+				// So we just pass a list of groups, each being defined by a class and a list of keys
+				// in order to avoid redoing the impact computation which is expensive
+				$aObjects = $oNode->GetObjects();
+				$aKeys = array();
+				foreach($aObjects as $oObj)
+				{
+					$sClass = get_class($oObj);
+					$aKeys[] = $oObj->GetKey();
+				}
+				$aData['groups'][$iGroupIdx] = array('class' => $sClass, 'keys' => $aKeys);
 				$oNode->SetProperty('group_index', $iGroupIdx);
 				$iGroupIdx++;
 			}

+ 73 - 9
js/simple_graph.js

@@ -61,6 +61,7 @@ $(function()
 			this.iTextHeight = 12;
 			this.fSliderZoom = 1.0;
 			this.bInUpdateSliderZoom = false;
+			this.bRedrawNeeded = false;
 			
 			this.oPaper = Raphael(this.element.get(0), 16*this.element.width(), 16*this.element.height());
 
@@ -71,6 +72,17 @@ $(function()
 			
 			this._create_toolkit_menu();
 			this._build_context_menus();
+			this.sTabId = null;
+			var jTabPanel = this.element.closest('.ui-tabs-panel');
+			if (jTabPanel.length > 0)
+			{
+				// We are inside a tab, find out which one and hook its activation
+				this.sTabId = jTabPanel.attr('id');
+				var jTabs = this.element.closest('.ui-tabs');
+				jTabs.on( "tabsactivate", function( event, ui ) {
+					me._on_tabs_activate(ui);
+				});					
+			}
 			$(window).bind('resized', function() { var that = me; window.setTimeout(function() { that._on_resize(); }, 50); } );
 			this.element.bind('mousewheel', function(event, delta, deltaX, deltaY) {
 			    return me._on_mousewheel(event, delta, deltaX, deltaY);
@@ -341,9 +353,8 @@ $(function()
 			}
 			return null;
 		},
-		auto_scale: function()
+		adjust_height: function()
 		{
-			var fMaxZoom = 1.5;
 			var maxHeight = this.element.parent().height();
 			// Compute the available height
 			var element = this.element;
@@ -355,6 +366,12 @@ $(function()
 			});
 			
 			this.element.height(maxHeight - 20);
+			this.oPaper.setSize(this.element.width(), this.element.height());
+		},
+		auto_scale: function()
+		{
+			var fMaxZoom = 1.5;
+			this.adjust_height();
 			
 			iMargin = 10;
 			xmin = this.options.xmin - iMargin;
@@ -639,10 +656,24 @@ $(function()
 		{
 			this.element.closest('.ui-tabs').tabs({ heightStyle: "fill" });
 			this.auto_scale();
-			this.oPaper.setSize(this.element.width(), this.element.height());
 			this._close_all_tooltips();
 			this.draw();
 		},
+		_on_tabs_activate: function(ui)
+		{
+			if (ui.newPanel.selector == ('#'+this.sTabId))
+			{
+				if (this.bRedrawNeeded)
+				{
+					this._updateBBox();
+					this.auto_scale();
+					this.oPaper.setSize(this.element.width(), this.element.height());
+					this._reset_pan_and_zoom();
+					this.draw();
+					bRedrawNeeded = false;
+				}
+			}
+		},
 		load: function(oData)
 		{
 			var me = this;
@@ -657,11 +688,43 @@ $(function()
 			{
 				this.add_edge(oData.edges[k]);
 			}
-			this._updateBBox();
-			this.auto_scale();
-			this.oPaper.setSize(this.element.width(), this.element.height());
-			this._reset_pan_and_zoom();
-			this.draw();
+			if (oData.groups)
+			{
+				this.refresh_groups(oData.groups);
+			}
+			if (this.element.is(':visible'))
+			{
+				this._updateBBox();
+				this.auto_scale();
+				this._reset_pan_and_zoom();
+				this.draw();
+			}
+			else
+			{
+				this.bRedrawNeeded = true;
+			}
+		},
+		refresh_groups: function(aGroups)
+		{
+			if ($('#impacted_groups').length > 0)
+			{
+				
+				// The "Groups" tab is present, refresh it
+				if (aGroups.length == 0)
+				{
+					this.element.closest('.ui-tabs').tabs("disable", 2);
+					$('#impacted_groups').html('');
+				}
+				else
+				{
+					this.element.closest('.ui-tabs').tabs("enable", 2);
+					$('#impacted_groups').html('<img src="../images/indicator.gif">');
+					var sUrl = GetAbsoluteUrlAppRoot()+'pages/ajax.render.php';
+					$.post(sUrl, { operation: 'relation_groups', groups: aGroups }, function(data) {
+						$('#impacted_groups').html(data);
+					});
+				}
+			}
 		},
 		_reset_pan_and_zoom: function()
 		{
@@ -688,11 +751,12 @@ $(function()
 			var aContexts = [];
 			$('#'+sId+'_contexts').multiselect('getChecked').each(function() { aContexts[$(this).val()] = me.options.additional_contexts[$(this).val()].oql; });
 			this.element.closest('.ui-tabs').tabs({ heightStyle: "fill" });
+			this.adjust_height();
 			this._close_all_tooltips();
 			this.oPaper.rect(this.xPan, this.yPan, this.element.width(), this.element.height()).attr({fill: '#000', opacity: 0.4, 'stroke-width': 0});
 			this.oPaper.rect(this.xPan + this.element.width()/2 - 100, this.yPan + this.element.height()/2 - 10, 200, 20)
 			.attr({fill: 'url(../setup/orange-progress.gif)', stroke: '#000', 'stroke-width': 1});
-			this.oPaper.text(this.xPan + this.element.width()/2, this.yPan + this.element.height()/2 - 20, this.options.labels.loading);
+			this.oPaper.text(this.xPan + this.element.width()/2, this.yPan + this.element.height()/2 - 20, this.options.labels.loading);			
 			
 			$('#'+sId+'_refresh_btn').button('disable'); 
 			$.post(sUrl, {excluded_classes: this.options.excluded_classes, g: this.options.grouping_threshold, sources: this.options.sources, excluded: this.options.excluded, contexts: aContexts, context_key: this.options.context_key }, function(data) {

+ 14 - 37
pages/UI.php

@@ -244,27 +244,14 @@ function DisplayNavigatorListTab($oP, $aResults, $sRelation, $sDirection, $oObj)
 	$oP->add("</div>");
 }
 
-function DisplayNavigatorGroupTab($oP, $aGroups, $sRelation, $oObj)
+function DisplayNavigatorGroupTab($oP)
 {
-	if (count($aGroups) > 0)
-	{
-		$oP->SetCurrentTab(Dict::S('UI:RelationGroups'));
-		$oP->add("<div id=\"impacted_groups\" style=\"width:100%;background-color:#fff;padding:10px;\">");
-		$iBlock = 1; // Zero is not a valid blockid
-		foreach($aGroups as $idx => $aObjects)
-		{
-			$sListClass = get_class(current($aObjects));
-			$oSet = CMDBObjectSet::FromArray($sListClass, $aObjects);
-			$oP->add("<h1>".Dict::Format('UI:RelationGroupNumber_N', (1+$idx))."</h1>\n");
-			$oP->add("<div id=\"relation_group_$idx\" class=\"page_header\">\n");
-			$oP->add("<h2>".MetaModel::GetClassIcon($sListClass)."&nbsp;<span class=\"hilite\">".Dict::Format('UI:Search:Count_ObjectsOf_Class_Found', count($aObjects), Metamodel::GetName($sListClass))."</h2>\n");
-			$oP->add("</div>\n");
-			$oBlock = DisplayBlock::FromObjectSet($oSet, 'list');
-			$oBlock->Display($oP, 'group_'.$iBlock++);
-			$oP->p('&nbsp;'); // Some space ?
-		}
-		$oP->add("</div>");
-	}
+	$oP->SetCurrentTab(Dict::S('UI:RelationGroups'));
+	$oP->add("<div id=\"impacted_groups\" style=\"width:100%;background-color:#fff;padding:10px;\">");
+	/*
+	 * Content is rendered asynchronously via pages/ajax.render.php?operation=relation_groups
+	*/
+	$oP->add("</div>");
 }
 
 /***********************************************************************************
@@ -1468,6 +1455,9 @@ EOF
 		$oObj = MetaModel::GetObject($sClass, $id);
 		$iMaxRecursionDepth = MetaModel::GetConfig()->Get('relations_max_depth', 20);
 		$aSourceObjects = array($oObj);
+		
+		$oP->set_title(MetaModel::GetRelationDescription($sRelation).' '.$oObj->GetName());
+		
 		if ($sRelation == 'depends on')
 		{
 			$sRelation = 'impacts';
@@ -1484,20 +1474,7 @@ EOF
 		
 
 		$aResults = $oRelGraph->GetObjectsByClass();
-		$oDisplayGraph = DisplayableGraph::FromRelationGraph($oRelGraph, $iGroupingThreshold, ($sDirection == 'down'));
-		
-		$aGroups = array();
-		$iGroupIdx = 0;
-		$oIterator = new RelationTypeIterator($oDisplayGraph, 'Node');
-		foreach($oIterator as $oNode)
-		{
-			if ($oNode instanceof DisplayableGroupNode)
-			{
-				$aGroups[] = $oNode->GetObjects();
-				$oNode->SetProperty('group_index', $iGroupIdx);
-				$iGroupIdx++;
-			}
-		}		
+		$oDisplayGraph = DisplayableGraph::FromRelationGraph($oRelGraph, $iGroupingThreshold, ($sDirection == 'down'));		
 		
 		$oP->AddTabContainer('Navigator');
 		$oP->SetCurrentTabContainer('Navigator');
@@ -1526,14 +1503,14 @@ EOF
 			DisplayNavigatorListTab($oP, $aResults, $sRelation, $sDirection, $oObj);
 			$oP->SetCurrentTab(Dict::S('UI:RelationshipGraph'));
 			$oDisplayGraph->Display($oP, $aResults, $sRelation, $oAppContext, array(), $sClassForAttachment, $iIdForAttachment, $sContextKey, array('this' => $oObj));
-			DisplayNavigatorGroupTab($oP, $aGroups, $sRelation, $oObj);
+			DisplayNavigatorGroupTab($oP);
 		}
 		else
 		{
 			$oP->SetCurrentTab(Dict::S('UI:RelationshipGraph'));
 			$oDisplayGraph->Display($oP, $aResults, $sRelation, $oAppContext, array(), $sClassForAttachment, $iIdForAttachment, $sContextKey, array('this' => $oObj));
 			DisplayNavigatorListTab($oP, $aResults, $sRelation, $sDirection, $oObj);
-			DisplayNavigatorGroupTab($oP, $aGroups, $sRelation, $oObj);
+			DisplayNavigatorGroupTab($oP);
 		}
 
 		$oP->SetCurrentTab('');
@@ -1622,7 +1599,7 @@ catch(Exception $e)
 	require_once(APPROOT.'/setup/setuppage.class.inc.php');
 	$oP = new SetupPage(Dict::S('UI:PageTitle:FatalError'));
 	$oP->add("<h1>".Dict::S('UI:FatalErrorMessage')."</h1>\n");	
-	$oP->error(Dict::Format('UI:Error_Details', $e->getMessage()));	
+	$oP->error(Dict::Format('UI:Error_Details', $e->getMessage()));		
 	$oP->output();
 
 	if (MetaModel::IsLogEnabledIssue())

+ 18 - 0
pages/ajax.render.php

@@ -1992,6 +1992,24 @@ EOF
 		$oPage->SetContentType('application/json');
 		break;
 		
+		case 'relation_groups':
+		$aGroups = utils::ReadParam('groups');
+		$iBlock = 1; // Zero is not a valid blockid
+		foreach($aGroups as $idx => $aDefinition)
+		{
+			$sListClass = $aDefinition['class'];
+			$oSearch = new DBObjectSearch($sListClass);
+			$oSearch->AddCondition('id', $aDefinition['keys'], 'IN');		
+			$oPage->add("<h1>".Dict::Format('UI:RelationGroupNumber_N', (1+$idx))."</h1>\n");
+			$oPage->add("<div id=\"relation_group_$idx\" class=\"page_header\">\n");
+			$oPage->add("<h2>".MetaModel::GetClassIcon($sListClass)."&nbsp;<span class=\"hilite\">".Dict::Format('UI:Search:Count_ObjectsOf_Class_Found', count($aDefinition['keys']), Metamodel::GetName($sListClass))."</h2>\n");
+			$oPage->add("</div>\n");
+			$oBlock = new DisplayBlock($oSearch, 'list');
+			$oBlock->Display($oPage, 'group_'.$iBlock++);
+			$oPage->p('&nbsp;'); // Some space ?
+		}
+		break;
+		
 		case 'ticket_impact':
 		require_once(APPROOT.'core/simplegraph.class.inc.php');
 		require_once(APPROOT.'core/relationgraph.class.inc.php');