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();