Преглед изворни кода

#516 and #517 Improved the export (specify fields for multi-column queries) and web queries (default field list) (reintegrated from branch 1.2)

git-svn-id: http://svn.code.sf.net/p/itop/code/trunk@1787 a333f486-631f-4898-b8df-5754b55c2be0
romainq пре 13 година
родитељ
комит
f09e159d60

+ 85 - 18
application/cmdbabstract.class.inc.php

@@ -655,8 +655,26 @@ abstract class cmdbAbstractObject extends CMDBObject implements iDisplay
 		$bTruncated = isset($aExtraParams['truncated']) ? $aExtraParams['truncated'] == true : true;
 		$bSelectMode = isset($aExtraParams['selection_mode']) ? $aExtraParams['selection_mode'] == true : false;
 		$bSingleSelectMode = isset($aExtraParams['selection_type']) ? ($aExtraParams['selection_type'] == 'single') : false;
-		$aExtraFields = isset($aExtraParams['extra_fields']) ? explode(',', trim($aExtraParams['extra_fields'])) : array();
-		
+
+		$aExtraFieldsRaw = isset($aExtraParams['extra_fields']) ? explode(',', trim($aExtraParams['extra_fields'])) : array();
+		$aExtraFields = array();
+		foreach ($aExtraFieldsRaw as $sFieldName)
+		{
+			// Ignore attributes not of the main queried class
+			if (preg_match('/^(.*)\.(.*)$/', $sFieldName, $aMatches))
+			{
+				$sClassAlias = $aMatches[1];
+				$sAttCode = $aMatches[2];
+				if ($sClassAlias == $oSet->GetFilter()->GetClassAlias())
+				{
+					$aExtraFields[] = $sAttCode;
+				}
+			}
+			else
+			{
+				$aExtraFields[] = $sFieldName;
+			}
+		}
 		$sHtml = '';
 		$oAppContext = new ApplicationContext();
 		$sClassName = $oSet->GetFilter()->GetClass();
@@ -942,7 +960,28 @@ EOF
 		$bDisplayMenu = isset($aExtraParams['menu']) ? $aExtraParams['menu'] == true : true;
 		// Check if there is a list of aliases to limit the display to...
 		$aDisplayAliases = isset($aExtraParams['display_aliases']) ? explode(',', $aExtraParams['display_aliases']) : array();
-		
+		$sZListName = isset($aExtraParams['zlist']) ? ($aExtraParams['zlist']) : 'list';
+
+		$aExtraFieldsRaw = isset($aExtraParams['extra_fields']) ? explode(',', trim($aExtraParams['extra_fields'])) : array();
+		$aExtraFields = array();
+		foreach ($aExtraFieldsRaw as $sFieldName)
+		{
+			// Ignore attributes not of the main queried class
+			if (preg_match('/^(.*)\.(.*)$/', $sFieldName, $aMatches))
+			{
+				$sClassAlias = $aMatches[1];
+				$sAttCode = $aMatches[2];
+				if (array_key_exists($sClassAlias, $oSet->GetSelectedClasses()))
+				{
+					$aExtraFields[$sClassAlias][] = $sAttCode;
+				}
+			}
+			else
+			{
+				$aExtraFields['*'] = $sAttCode;
+			}
+		}
+
 		$sHtml = '';
 		$oAppContext = new ApplicationContext();
 		$aClasses = $oSet->GetFilter()->GetSelectedClasses();
@@ -958,12 +997,36 @@ EOF
 		$aAttribs = array();
 		foreach($aAuthorizedClasses as $sAlias => $sClassName) // TO DO: check if the user has enough rights to view the classes of the list...
 		{
-			$aList[$sClassName] = MetaModel::GetZListItems($sClassName, 'list');
+			if (array_key_exists($sAlias, $aExtraFields))
+			{
+				$aList[$sAlias] = $aExtraFields[$sAlias];
+			}
+			else
+			{
+				$aList[$sAlias] = array();
+			}
+			if ($sZListName !== false)
+			{
+				$aDefaultList = self::FlattenZList(MetaModel::GetZListItems($sClassName, $sZListName));
+				
+				$aList[$sAlias] = array_merge($aDefaultList, $aList[$sAlias]);
+			}
+	
+			// Filter the list to removed linked set since we are not able to display them here
+			foreach($aList[$sAlias] as $index => $sAttCode)
+			{
+				$oAttDef = MetaModel::GetAttributeDef($sClassName, $sAttCode);
+				if ($oAttDef instanceof AttributeLinkedSet)
+				{
+					// Removed from the display list
+					unset($aList[$sAlias][$index]);
+				}
+			}
 			if ($bViewLink)
 			{
 				$aAttribs['key_'.$sAlias] = array('label' => MetaModel::GetName($sClassName), 'description' => '');
 			}
-			foreach($aList[$sClassName] as $sAttCode)
+			foreach($aList[$sAlias] as $sAttCode)
 			{
 				$aAttribs[$sAttCode.'_'.$sAlias] = array('label' => MetaModel::GetLabel($sClassName, $sAttCode), 'description' => MetaModel::GetDescription($sClassName, $sAttCode));
 			}
@@ -972,7 +1035,7 @@ EOF
 		$aAttToLoad = array(); // attributes to load
 		foreach($aAuthorizedClasses as $sAlias => $sClassName)
 		{
-			foreach($aList[$sClassName] as $sAttCode)
+			foreach($aList[$sAlias] as $sAttCode)
 			{
 				$aAttToLoad[$sAlias][] = $sAttCode;
 			}
@@ -1006,7 +1069,7 @@ EOF
 						$aRow['key_'.$sAlias] = $aObjects[$sAlias]->GetHyperLink();
 					}
 				}
-				foreach($aList[$sClassName] as $sAttCode)
+				foreach($aList[$sAlias] as $sAttCode)
 				{
 					if (is_null($aObjects[$sAlias]))
 					{
@@ -1089,6 +1152,8 @@ EOF
 		$aHeader = array();
 		foreach($aAuthorizedClasses as $sAlias => $sClassName)
 		{
+			$aList[$sAlias] = array();
+
 			foreach(MetaModel::ListAttributeDefs($sClassName) as $sAttCode => $oAttDef)
 			{
 				if (is_null($aFields) || (count($aFields) == 0))
@@ -1096,20 +1161,20 @@ EOF
 					// Standard list of attributes (no link sets)
 					if ($oAttDef->IsScalar() && ($oAttDef->IsWritable() || $oAttDef->IsExternalField()))
 					{
-						$aList[$sClassName][$sAttCode] = $oAttDef;
+						$aList[$sAlias][$sAttCode] = $oAttDef;
 					}
 				}
 				else
 				{
 					// User defined list of attributes
-					if (in_array($sAttCode, $aFields))
+					if (in_array($sAttCode, $aFields) || in_array($sAlias.'.'.$sAttCode, $aFields))
 					{
-						$aList[$sClassName][$sAttCode] = $oAttDef;
+						$aList[$sAlias][$sAttCode] = $oAttDef;
 					}
 				}
 			}
 			$aHeader[] = 'id';
-			foreach($aList[$sClassName] as $sAttCode => $oAttDef)
+			foreach($aList[$sAlias] as $sAttCode => $oAttDef)
 			{
 				$sStar = '';
 				if ($oAttDef->IsExternalField())
@@ -1156,7 +1221,7 @@ EOF
 				{
 					$aRow[] = $oObj->GetKey();
 				}
-				foreach($aList[$sClassName] as $sAttCode => $oAttDef)
+				foreach($aList[$sAlias] as $sAttCode => $oAttDef)
 				{
 					if (is_null($oObj))
 					{
@@ -1203,6 +1268,8 @@ EOF
 		$aHeader = array();
 		foreach($aAuthorizedClasses as $sAlias => $sClassName)
 		{
+			$aList[$sAlias] = array();
+
 			foreach(MetaModel::ListAttributeDefs($sClassName) as $sAttCode => $oAttDef)
 			{
 				if (is_null($aFields) || (count($aFields) == 0))
@@ -1210,20 +1277,20 @@ EOF
 					// Standard list of attributes (no link sets)
 					if ($oAttDef->IsScalar() && ($oAttDef->IsWritable() || $oAttDef->IsExternalField()))
 					{
-						$aList[$sClassName][$sAttCode] = $oAttDef;
+						$aList[$sAlias][$sAttCode] = $oAttDef;
 					}
 				}
 				else
 				{
 					// User defined list of attributes
-					if (in_array($sAttCode, $aFields))
+					if (in_array($sAttCode, $aFields) || in_array($sAlias.'.'.$sAttCode, $aFields))
 					{
-						$aList[$sClassName][$sAttCode] = $oAttDef;
+						$aList[$sAlias][$sAttCode] = $oAttDef;
 					}
 				}
 			}
 			$aHeader[] = 'id';
-			foreach($aList[$sClassName] as $sAttCode => $oAttDef)
+			foreach($aList[$sAlias] as $sAttCode => $oAttDef)
 			{
 				$sStar = '';
 				if ($oAttDef->IsExternalField())
@@ -1284,7 +1351,7 @@ EOF
 				{
 					$aRow[] = '<td>'.$oObj->GetKey().'</td>';
 				}
-				foreach($aList[$sClassName] as $sAttCode => $oAttDef)
+				foreach($aList[$sAlias] as $sAttCode => $oAttDef)
 				{
 					if (is_null($oObj))
 					{
@@ -1328,7 +1395,7 @@ EOF
 		}
 		$aAttribs = array();
 		$aList = array();
-		$aList[$sClassName] = MetaModel::GetZListItems($sClassName, 'details');
+		$aList[$sAlias] = MetaModel::GetZListItems($sClassName, 'details');
 		$oPage->add("<Set>\n");
 		$oSet->Seek(0);
 		while ($aObjects = $oSet->FetchAssoc())

+ 4 - 2
application/query.class.inc.php

@@ -46,8 +46,10 @@ abstract class Query extends cmdbAbstractObject
 		MetaModel::Init_AddAttribute(new AttributeString("name", array("allowed_values"=>null, "sql"=>"name", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
 		MetaModel::Init_AddAttribute(new AttributeText("description", array("allowed_values"=>null, "sql"=>"description", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
 
+		MetaModel::Init_AddAttribute(new AttributeString("fields", array("allowed_values"=>null, "sql"=>"fields", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
+
 		// Display lists
-		MetaModel::Init_SetZListItems('details', array('name', 'description')); // Attributes to be displayed for the complete details
+		MetaModel::Init_SetZListItems('details', array('name', 'description', 'fields')); // Attributes to be displayed for the complete details
 		MetaModel::Init_SetZListItems('list', array('description')); // Attributes to be displayed for a list
 		// Search criteria
 //		MetaModel::Init_SetZListItems('standard_search', array('name')); // Criteria of the std search form
@@ -76,7 +78,7 @@ class QueryOQL extends Query
 		MetaModel::Init_AddAttribute(new AttributeOQL("oql", array("allowed_values"=>null, "sql"=>"oql", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
 
 		// Display lists
-		MetaModel::Init_SetZListItems('details', array('name', 'description', 'oql')); // Attributes to be displayed for the complete details
+		MetaModel::Init_SetZListItems('details', array('name', 'description', 'oql', 'fields')); // Attributes to be displayed for the complete details
 		MetaModel::Init_SetZListItems('list', array('description')); // Attributes to be displayed for a list
 		// Search criteria
 //		MetaModel::Init_SetZListItems('standard_search', array('name')); // Criteria of the std search form

+ 12 - 1
core/dbobjectsearch.class.php

@@ -73,7 +73,18 @@ class DBObjectSearch
 	public function IsDataFiltered() {return $this->m_bDataFiltered; }
 	public function SetDataFiltered() {$this->m_bDataFiltered = true;}
 
-	public function GetClassName($sAlias) {return $this->m_aClasses[$sAlias];}
+	public function GetClassName($sAlias)
+	{
+		if (array_key_exists($sAlias, $this->m_aClasses))
+		{
+			return $this->m_aClasses[$sAlias];
+		}
+		else
+		{
+			throw new CoreException("Invalid class alias '$sAlias'");
+		}
+	}
+
 	public function GetJoinedClasses() {return $this->m_aClasses;}
 
 	public function GetClass()

+ 2 - 0
dictionaries/dictionary.itop.ui.php

@@ -87,6 +87,8 @@ Dict::Add('EN US', 'English', 'English', array(
 	'Class:Query/Attribute:name+' => 'Identifies the query',
 	'Class:Query/Attribute:description' => 'Description',
 	'Class:Query/Attribute:description+' => 'Long description for the query (purpose, usage, etc.)',
+	'Class:Query/Attribute:fields' => 'Fields',
+	'Class:Query/Attribute:fields+' => 'Coma separated list of attributes (or alias.attribute) to export',
 
 	'Class:QueryOQL' => 'OQL Query',
 	'Class:QueryOQL+' => 'A query based on the Object Query Language',

+ 2 - 0
dictionaries/fr.dictionary.itop.ui.php

@@ -56,6 +56,8 @@ Dict::Add('FR FR', 'French', 'Français', array(
 	'Class:Query/Attribute:name+' => 'Identification de la requête',
 	'Class:Query/Attribute:description' => 'Description',
 	'Class:Query/Attribute:description+' => 'Description complète (finalité, utilisations, public)',
+	'Class:Query/Attribute:fields' => 'Champs',
+	'Class:Query/Attribute:fields+' => 'Liste CSV des attributs (ou alias.attribut) à exporter',
 	'Class:QueryOQL' => 'Requête OQL',
 	'Class:QueryOQL+' => 'Une requête écrite dans le langage "Object Query Language"',
 	'Class:QueryOQL/Attribute:oql' => 'Expression',

+ 42 - 6
webservices/export.php

@@ -76,6 +76,8 @@ $currentOrganization = utils::ReadParam('org_id', '');
 
 // Main program
 $sExpression = utils::ReadParam('expression', '', true /* Allow CLI */, 'raw_data');
+$sFields = trim(utils::ReadParam('fields', '', true, 'raw_data')); // CSV field list (allows to specify link set attributes, still not taken into account for XML export)
+
 if (strlen($sExpression) == 0)
 {
 	$sQueryId = trim(utils::ReadParam('query', '', true /* Allow CLI */, 'raw_data'));
@@ -87,14 +89,17 @@ if (strlen($sExpression) == 0)
 		{
 			$oQuery = $oQueries->Fetch();
 			$sExpression = $oQuery->Get('oql');
+			if (strlen($sFields) == 0)
+			{
+				$sFields = trim($oQuery->Get('fields'));
+			} 
 		}
 	}
 }
 
-
 $sFormat = strtolower(utils::ReadParam('format', 'html', true /* Allow CLI */));
 
-$sFields = utils::ReadParam('fields', '', true, 'raw_data'); // CSV field list (allows to specify link set attributes, still not taken into account for XML export)
+
 $aFields = explode(',', $sFields);
 // Clean the list of columns (empty it if every string is empty)
 foreach($aFields as $index => $sField)
@@ -114,6 +119,30 @@ if (!empty($sExpression))
 	{
 		$oFilter = DBObjectSearch::FromOQL($sExpression);
 
+		// Check and adjust column names
+		//
+		foreach($aFields as $index => $sField)
+		{
+			if (preg_match('/^(.*)\.(.*)$/', $sField, $aMatches))
+			{
+				$sClassAlias = $aMatches[1];
+				$sAttCode = $aMatches[2];
+			}
+			else
+			{
+				$sClassAlias = $oFilter->GetClassAlias();
+				$sAttCode = $sField;
+				$aFields[$index] = $sClassAlias.'.'.$sAttCode;
+			}
+			$sClass = $oFilter->GetClassName($sClassAlias);
+			if (!MetaModel::IsValidAttCode($sClass, $sAttCode))
+			{
+				throw new CoreException("Invalid field specification $sField: $sAttCode is not a valid attribute for $sClass");
+			}
+		}
+
+		// Read query parameters
+		//
 		$aArgs = array();
 		foreach($oFilter->GetQueryParams() as $sParam => $foo)
 		{
@@ -195,15 +224,22 @@ if (!empty($sExpression))
 				
 				default:
 				$oP = new WebPage("iTop - Export");
-				$oP->add("Unsupported format '$sFormat'. Possible values are: html, csv or xml.");
+				$oP->add("Unsupported format '$sFormat'. Possible values are: html, csv, spreadsheet or xml.");
 			}
 		}
 	}
 	catch(Exception $e)
 	{
 		$oP = new WebPage("iTop - Export");
-		$oP->p("Error the query can not be executed.");		
-		$oP->p($e->GetHtmlDesc());		
+		$oP->p("Error the query can not be executed.");
+		if ($e instanceof CoreException)
+		{		
+			$oP->p($e->GetHtmlDesc());
+		}
+		else
+		{
+			$oP->p($e->getMessage());
+		}		
 	}
 }
 if (!$oP)
@@ -224,7 +260,7 @@ if (!$oP)
 	$oP->p(" * query: (alternative to 'expression') the id of an entry from the query phrasebook");
 	$oP->p(" * arg_xxx: (needed if the query has parameters) the value of the parameter 'xxx'");
 	$oP->p(" * format: (optional, default is html) the desired output format. Can be one of 'html', 'spreadsheet', 'csv' or 'xml'");
-	$oP->p(" * fields: (optional, no effect on XML format) list of fields (attribute codes) separated by a coma");
+	$oP->p(" * fields: (optional, no effect on XML format) list of fields (attribute codes, or alias.attcode) separated by a coma");
 }
 
 $oP->output();