From 5767d14c3efd2c7331cece0f7e19a56bd830c6ba Mon Sep 17 00:00:00 2001 From: Romain Quetiez Date: Wed, 18 Jan 2012 17:09:53 +0000 Subject: [PATCH] #516 and #517 Improved the export (specify fields for multi-column queries) and web queries (default field list) (reintegrated from branch 1.2) SVN:trunk[1787] --- application/cmdbabstract.class.inc.php | 103 ++++++++++++++++++++----- application/query.class.inc.php | 6 +- core/dbobjectsearch.class.php | 13 +++- dictionaries/dictionary.itop.ui.php | 2 + dictionaries/fr.dictionary.itop.ui.php | 2 + webservices/export.php | 48 ++++++++++-- 6 files changed, 147 insertions(+), 27 deletions(-) diff --git a/application/cmdbabstract.class.inc.php b/application/cmdbabstract.class.inc.php index 660cf25d6..c18d20530 100644 --- a/application/cmdbabstract.class.inc.php +++ b/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[] = ''.$oObj->GetKey().''; } - 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("\n"); $oSet->Seek(0); while ($aObjects = $oSet->FetchAssoc()) diff --git a/application/query.class.inc.php b/application/query.class.inc.php index cf7bc1af3..d8f0e7c68 100644 --- a/application/query.class.inc.php +++ b/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 diff --git a/core/dbobjectsearch.class.php b/core/dbobjectsearch.class.php index 28a8872ab..0d6c54247 100644 --- a/core/dbobjectsearch.class.php +++ b/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() diff --git a/dictionaries/dictionary.itop.ui.php b/dictionaries/dictionary.itop.ui.php index 59f2041c9..e73fb8e98 100644 --- a/dictionaries/dictionary.itop.ui.php +++ b/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', diff --git a/dictionaries/fr.dictionary.itop.ui.php b/dictionaries/fr.dictionary.itop.ui.php index 881e0b213..87192b259 100644 --- a/dictionaries/fr.dictionary.itop.ui.php +++ b/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', diff --git a/webservices/export.php b/webservices/export.php index eb1e57369..342b28270 100644 --- a/webservices/export.php +++ b/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();