瀏覽代碼

Exports further improved:
- Support reconciliation keys for every external key
- Better support for Case logs and multiline text fields (both in the preview and in the results)
- Do not repeat identical columns in the list of proposed columns. Examples with UserRequest: friendlyname is equivalent to ref, UserRequest::caller_name is equivalent to UserRequest::caller_id->name
- Optimized the preview for huge data sets (OptimizeColumnLoad)
- Cosmetics on the preview
- Labels for ids aligned with the labels used by the CSV import feature
- Fixed Stop Watch output for PDF/HTML/spreadsheet formats

git-svn-id: http://svn.code.sf.net/p/itop/code/trunk@3712 a333f486-631f-4898-b8df-5754b55c2be0

romainq 9 年之前
父節點
當前提交
b2618d03ae

+ 3 - 1
application/pdfpage.class.inc.php

@@ -102,9 +102,11 @@ class PDFPage extends WebPage
 	{
 		$this->add_style(
 <<<EOF
+table {
+	padding: 2pt;
+}
 table.listResults td {
 	border: 0.5pt solid #000 ;
-	padding: 0.5pt;
 }
 table.listResults th {
 	background-color: #eee;

+ 1 - 1
application/xlsxwriter.class.php

@@ -220,7 +220,7 @@ Class XLSXWriter
 		//fwrite($fd, 		'<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="1" numFmtId="9"/>');
 		fwrite($fd, 	'</cellStyleXfs>');
 		fwrite($fd, 	'<cellXfs count="4">');
-		fwrite($fd, 		'<xf applyAlignment="false" applyBorder="false" applyFont="false" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="164" xfId="0"/>');
+		fwrite($fd, 		'<xf applyAlignment="1" applyBorder="false" applyFont="false" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="164" xfId="0"><alignment wrapText="1"/></xf>');
 		fwrite($fd, 		'<xf applyAlignment="false" applyBorder="false" applyFont="false" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="165" xfId="0"/>');
 		fwrite($fd, 		'<xf applyAlignment="false" applyBorder="false" applyFont="false" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="166" xfId="0"/>');
 		fwrite($fd, 		'<xf applyAlignment="false" applyBorder="false" applyFont="false" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="167" xfId="0"/>');

+ 5 - 1
core/bulkexport.class.inc.php

@@ -281,9 +281,13 @@ abstract class BulkExport
 		return array(); // return array('csv' => Dict::S('UI:ExportFormatCSV'));
 	}
 	
+
+	public function SetHttpHeaders(WebPage $oPage)
+	{
+	}
+
 	public function GetHeader()
 	{
-		
 	}
 	abstract public function GetNextChunk(&$aStatus);
 	public function GetFooter()

+ 14 - 8
core/csvbulkexport.class.inc.php

@@ -58,7 +58,7 @@ class CSVBulkExport extends TabularBulkExport
 	}
 
 
-	protected function SuggestField($aAliases, $sClass, $sAlias, $sAttCode)
+	protected function SuggestField($sClass, $sAttCode)
 	{
 		switch($sAttCode)
 		{
@@ -74,7 +74,7 @@ class CSVBulkExport extends TabularBulkExport
 				}
 		}
 
-		return parent::SuggestField($aAliases, $sClass, $sAlias, $sAttCode);
+		return parent::SuggestField($sClass, $sAttCode);
 	}
 
 	public function EnumFormParts()
@@ -170,13 +170,19 @@ class CSVBulkExport extends TabularBulkExport
 
 	protected function GetSampleData($oObj, $sAttCode)
 	{
-		if ($oObj)
-		{
-			$sRet = trim($oObj->GetAsCSV($sAttCode), '"');
-		}
-		else
+		return '<div class="text-preview">'.htmlentities($this->GetValue($oObj, $sAttCode), ENT_QUOTES, 'UTF-8').'</div>';
+	}
+
+	protected function GetValue($oObj, $sAttCode)
+	{
+		switch($sAttCode)
 		{
-			$sRet = '';
+			case 'id':
+				$sRet = $oObj->GetKey();
+				break;
+					
+			default:
+				$sRet = trim($oObj->GetAsCSV($sAttCode), '"');
 		}
 		return $sRet;
 	}

+ 1 - 1
core/dbsearch.class.php

@@ -392,7 +392,7 @@ abstract class DBSearch
 			$sRes = $oSQLQuery->RenderSelect($aOrderSpec, $aScalarArgs, $iLimitCount, $iLimitStart, $bGetCount, $bBeautifulSQL);
 			if ($sClassAlias == '_itop_')
 			{
-				echo $sRes."<br/>\n";
+				IssueLog::Info('SQL Query (_itop_): '.$sRes);
 			}
 		}
 		catch (MissingQueryArgument $e)

+ 37 - 26
core/excelbulkexport.class.inc.php

@@ -67,7 +67,7 @@ class ExcelBulkExport extends TabularBulkExport
 		}
 	}
 
-	protected function SuggestField($aAliases, $sClass, $sAlias, $sAttCode)
+	protected function SuggestField($sClass, $sAttCode)
 	{
 		switch($sAttCode)
 		{
@@ -83,7 +83,41 @@ class ExcelBulkExport extends TabularBulkExport
 				}
 		}
 
-		return parent::SuggestField($aAliases, $sClass, $sAlias, $sAttCode);
+		return parent::SuggestField($sClass, $sAttCode);
+	}
+
+	protected function GetSampleData($oObj, $sAttCode)
+	{
+		return '<div class="text-preview">'.htmlentities($this->GetValue($oObj, $sAttCode), ENT_QUOTES, 'UTF-8').'</div>';
+	}
+
+	protected function GetValue($oObj, $sAttCode)
+	{
+		switch($sAttCode)
+		{
+			case 'id':
+				$sRet = $oObj->GetKey();
+				break;
+					
+			default:
+			$value = $oObj->Get($sAttCode);
+			if ($value instanceOf ormCaseLog)
+			{
+				// Extract the case log as text and remove the "===" which make Excel think that the cell contains a formula the next time you edit it!
+				$sRet = trim(preg_replace('/========== ([^=]+) ============/', '********** $1 ************', $value->GetText()));
+			}
+			else if ($value instanceOf DBObjectSet)
+			{
+				$oAttDef = MetaModel::GetAttributeDef(get_class($oObj), $sAttCode);
+				$sRet = $oAttDef->GetAsCSV($value, '', '', $oObj);
+			}
+			else
+			{
+				$oAttDef = MetaModel::GetAttributeDef(get_class($oObj), $sAttCode);
+				$sRet = $oAttDef->GetEditValue($value, $oObj);
+			}
+		}
+		return $sRet;
 	}
 
 	public function GetHeader()
@@ -155,30 +189,7 @@ class ExcelBulkExport extends TabularBulkExport
 				$sField = '';
 				if ($oObj)
 				{
-					switch($sAttCode)
-					{
-						case 'id':
-							$sField = $oObj->GetKey();
-							break;
-								
-						default:
-						$value = $oObj->Get($sAttCode);
-						if ($value instanceOf ormCaseLog)
-						{
-							// Extract the case log as text and remove the "===" which make Excel think that the cell contains a formula the next time you edit it!
-							$sField = trim(preg_replace('/========== ([^=]+) ============/', '********** $1 ************', $value->GetText()));
-						}
-						else if ($value instanceOf DBObjectSet)
-						{
-							$oAttDef = MetaModel::GetAttributeDef(get_class($oObj), $sAttCode);
-							$sField =  $oAttDef->GetAsCSV($value, '', '', $oObj);
-						}
-						else
-						{
-							$oAttDef = MetaModel::GetAttributeDef(get_class($oObj), $sAttCode);
-							$sField =  $oAttDef->GetEditValue($value, $oObj);
-						}
-					}
+					$sField = $this->GetValue($oObj, $sAttCode);
 				}
 				$aData[] = $sField;
 			}

+ 25 - 15
core/htmlbulkexport.class.inc.php

@@ -51,13 +51,31 @@ class HTMLBulkExport extends TabularBulkExport
 
 	protected function GetSampleData($oObj, $sAttCode)
 	{
-		if ($oObj)
-		{
-			$sRet = $oObj->GetAsHTML($sAttCode);
-		}
-		else
+		return $this->GetValue($oObj, $sAttCode);
+	}
+
+	protected function GetValue($oObj, $sAttCode)
+	{
+		switch($sAttCode)
 		{
-			$sRet = '';
+			case 'id':
+				$sRet = $oObj->GetHyperlink();
+				break;
+					
+			default:
+				$value = $oObj->Get($sAttCode);
+				if ($value instanceof ormCaseLog)
+				{
+					$sRet = $value->GetAsSimpleHtml();
+				}
+				elseif ($value instanceof ormStopWatch)
+				{
+					$sRet = $value->GetTimeSpent();
+				}
+				else
+				{
+					$sRet = $oObj->GetAsHtml($sAttCode);
+				}
 		}
 		return $sRet;
 	}
@@ -125,15 +143,7 @@ class HTMLBulkExport extends TabularBulkExport
 				$sField = '';
 				if ($oObj)
 				{
-					switch($sAttCode)
-					{
-						case 'id':
-							$sField = $aRow[$sAlias]->GetHyperlink();
-							break;
-								
-						default:
-							$sField = $aRow[$sAlias]->GetAsHtml($sAttCode);
-					}
+					$sField = $this->GetValue($oObj, $sAttCode);
 				}
 				$sValue = ($sField === '') ? '&nbsp;' : $sField;
 				$sData .= "<td>$sValue</td>";

+ 19 - 0
core/metamodel.class.php

@@ -429,6 +429,25 @@ abstract class MetaModel
 		return $oNameExpr;
 	}
 
+	/**
+	 * Returns the friendly name IIF it is equivalent to a single attribute	
+	 */	
+	final static public function GetFriendlyNameAttributeCode($sClass)
+	{
+		$aNameSpec = self::GetNameSpec($sClass);
+		$sFormat = trim($aNameSpec[0]);
+		$aAttributes = $aNameSpec[1];                                                     
+		if (($sFormat != '') && ($sFormat != '%1$s'))
+		{
+			return null;
+		}
+		if (count($aAttributes) > 1)
+		{
+			return null;
+		}
+		return reset($aAttributes);
+	}
+
 	final static public function GetStateAttributeCode($sClass)
 	{
 		self::_check_subclass($sClass);	

+ 75 - 0
core/ormcaselog.class.inc.php

@@ -213,6 +213,81 @@ class ormCaseLog {
 	}
 	
 	/**
+	 * Produces an HTML representation, aimed at being used to produce a PDF with TCPDF (no table)
+	 */	 	
+	public function GetAsSimpleHtml()
+	{
+		$sStyleCaseLogHeader = '';
+		$sStyleCaseLogEntry = '';
+
+		$sHtml = '<ul class="case_log_simple_html">';
+		$iPos = 0;
+		$aIndex = $this->m_aIndex;
+		for($index=count($aIndex)-1 ; $index >= 0 ; $index--)
+		{
+			$iPos += $aIndex[$index]['separator_length'];
+			$sTextEntry = substr($this->m_sLog, $iPos, $aIndex[$index]['text_length']);
+			$sTextEntry = str_replace(array("\r\n", "\n", "\r"), "<br/>", htmlentities($sTextEntry, ENT_QUOTES, 'UTF-8'));
+			$iPos += $aIndex[$index]['text_length'];
+
+			$sEntry = '<li>';
+			// Workaround: PHP < 5.3 cannot unserialize correctly DateTime objects,
+			// therefore we have changed the format. To preserve the compatibility with existing
+			// installations of iTop, both format are allowed:
+			//     the 'date' item is either a DateTime object, or a unix timestamp
+			if (is_int($aIndex[$index]['date']))
+			{
+				// Unix timestamp
+				$sDate = date(Dict::S('UI:CaseLog:DateFormat'),$aIndex[$index]['date']);
+			}
+			elseif (is_object($aIndex[$index]['date']))
+			{
+				if (version_compare(phpversion(), '5.3.0', '>='))
+				{
+					// DateTime
+					$sDate = $aIndex[$index]['date']->format(Dict::S('UI:CaseLog:DateFormat'));
+				}
+				else
+				{
+					// No Warning... but the date is unknown
+					$sDate = '';
+				}
+			}
+			$sEntry .= sprintf(Dict::S('UI:CaseLog:Header_Date_UserName'), '<span class="caselog_header_date">'.$sDate.'</span>', '<span class="caselog_header_user">'.$aIndex[$index]['user_name'].'</span>');
+			$sEntry .= '<div class="case_log_simple_html_entry" style="'.$sStyleCaseLogEntry.'">';
+			$sEntry .= $sTextEntry;
+			$sEntry .= '</div>';
+			$sEntry .= '</li>';
+			$sHtml = $sHtml.$sEntry;
+		}
+
+		// Process the case of an eventual remainder (quick migration of AttributeText fields)
+		if ($iPos < (strlen($this->m_sLog) - 1))
+		{
+			$sTextEntry = substr($this->m_sLog, $iPos);
+			$sTextEntry = str_replace(array("\r\n", "\n", "\r"), "<br/>", htmlentities($sTextEntry, ENT_QUOTES, 'UTF-8'));
+
+			if (count($this->m_aIndex) == 0)
+			{
+				$sHtml .= '<li>';
+				$sHtml .= $sTextEntry;
+				$sHtml .= '</li>';
+			}
+			else
+			{
+				$sHtml .= '<li>';
+				$sHtml .= Dict::S('UI:CaseLog:InitialValue');
+				$sHtml .= '<div class="case_log_simple_html_entry" style="'.$sStyleCaseLogEntry.'">';
+				$sHtml .= $sTextEntry;
+				$sHtml .= '</div>';
+				$sHtml .= '</li>';
+			}
+		}
+		$sHtml .= '</ul>';
+		return $sHtml;
+	}
+
+	/**
 	 * Produces an HTML representation, aimed at being used within the iTop framework
 	 */	 	
 	public function GetAsHTML(WebPage $oP = null, $bEditMode = false, $aTransfoHandler = null)

+ 3 - 0
core/pdfbulkexport.class.inc.php

@@ -124,6 +124,9 @@ class PDFBulkExport extends HTMLBulkExport
 		$sData = parent::GetFooter();
 
 		$oPage = new PDFPage(Dict::Format('Core:BulkExportOf_Class', MetaModel::GetName($this->oSearch->GetClass())), $this->aStatusInfo['page_size'], $this->aStatusInfo['page_orientation']);
+		$oPDF = $oPage->get_tcpdf();
+		$oPDF->SetFont('dejavusans', '', 8, '', true);
+
 		$oPage->add(file_get_contents($this->aStatusInfo['tmp_file']));
 		$oPage->add($sData);
 

+ 60 - 22
core/spreadsheetbulkexport.class.inc.php

@@ -63,17 +63,57 @@ class SpreadsheetBulkExport extends TabularBulkExport
 	
 	protected function GetSampleData($oObj, $sAttCode)
 	{
-		if ($oObj)
-		{
-			$sRet = $oObj->GetAsHTML($sAttCode);
-		}
-		else
+		return $this->GetValue($oObj, $sAttCode);
+	}
+
+	protected function GetValue($oObj, $sAttCode)
+	{
+		switch($sAttCode)
 		{
-			$sRet = '';
+			case 'id':
+				$sRet = $oObj->GetKey();
+				break;
+					
+			default:
+				$value = $oObj->Get($sAttCode);
+				$oAttDef = MetaModel::GetAttributeDef(get_class($oObj), $sAttCode);
+				if ($value instanceof ormCaseLog)
+				{
+					$sRet = str_replace("\n", "<br/>", htmlentities($value->__toString(), ENT_QUOTES, 'UTF-8'));
+				}
+				elseif ($value instanceof ormStopWatch)
+				{
+					$sRet = $value->GetTimeSpent();
+				}
+				elseif ($oAttDef instanceof AttributeString)
+				{
+					$sRet = $oObj->GetAsHTML($sAttCode);
+				}
+				else
+				{
+					if ($this->bLocalizeOutput)
+					{
+						$sRet = htmlentities($oObj->GetEditValue(), ENT_QUOTES, 'UTF-8');
+					}
+					else
+					{
+						$sRet = htmlentities($value, ENT_QUOTES, 'UTF-8');
+					}
+				}
 		}
+
 		return $sRet;
 	}
 
+	public function SetHttpHeaders(WebPage $oPage)
+	{
+		// Integration within MS-Excel web queries + HTTPS + IIS:
+		// MS-IIS set these header values with no-cache... while Excel fails to do the job if using HTTPS
+		// Then the fix is to force the reset of header values Pragma and Cache-control 
+		$oPage->add_header("Pragma:", true);
+		$oPage->add_header("Cache-control:", true);
+	}
+
 	public function GetHeader()
 	{
 		$oSet = new DBObjectSet($this->oSearch);
@@ -99,8 +139,14 @@ class SpreadsheetBulkExport extends TabularBulkExport
 					$aData[] = $sColLabel;
 				}
 			}
+			else
+			{
+				$aData[] = $sColLabel;
+			}
 		}
-		$sData = "<table border=\"1\">\n";
+		$sData = '';
+		$sData .= '<style>table br {mso-data-placement:same-cell;}</style>'; // Trick for Excel: keep line breaks inside the same cell !
+		$sData .= "<table border=\"1\">\n";
 		$sData .= "<tr>\n";
 		foreach($aData as $sLabel)
 		{
@@ -137,14 +183,14 @@ class SpreadsheetBulkExport extends TabularBulkExport
 				$oObj = $aRow[$sAlias];
 				if ($oObj == null)
 				{
-					$sData .= "<td x:str>$sField</td>";
+					$sData .= "<td x:str></td>";
 					continue;
 				}
 				
 				switch($sAttCode)
 				{
 					case 'id':
-					$sField = $oObj->GetName();
+					$sField = $oObj->GetKey();
 					$sData .= "<td>$sField</td>";
 					break;
 							
@@ -165,22 +211,14 @@ class SpreadsheetBulkExport extends TabularBulkExport
 						// Trick for Excel: treat the content as text even if it begins with an equal sign
 						$sData .= "<td x:str>$sField</td>";
 					}
+					else if($oAttDef instanceof AttributeString)
+					{
+						$sField = $oObj->GetAsHTML($sAttCode);
+						$sData .= "<td x:str>$sField</td>";
+					}
 					else
 					{
 						$rawValue = $oObj->Get($sAttCode);
-						// Due to custom formatting rules, empty friendlynames may be rendered as non-empty strings
-						// let's fix this and make sure we render an empty string if the key == 0
-						if ($oAttDef instanceof AttributeFriendlyName)
-						{
-							$sKeyAttCode = $oAttDef->GetKeyAttCode();
-							if ($sKeyAttCode != 'id')
-							{
-								if ($oObj->Get($sKeyAttCode) == 0)
-								{
-									$sValue = '';
-								}
-							}
-						}
 						if ($this->bLocalizeOutput)
 						{
 							$sField = htmlentities($oFinalAttDef->GetEditValue($rawValue), ENT_QUOTES, 'UTF-8');

+ 173 - 45
core/tabularbulkexport.class.inc.php

@@ -47,12 +47,13 @@ abstract class TabularBulkExport extends BulkExport
 				break;
 					
 			default:
-				return parent:: DisplayFormPart($oP, $sPartId);
+				return parent::DisplayFormPart($oP, $sPartId);
 		}
 	}
 
 	protected function SuggestFields($aSuggestedFields)
 	{
+		$aRet = array();
 		// By defaults all fields are Ok, nothing gets translated but
 		// you can overload this method if some fields are better exported
 		// (in a given format) by using an alternate field, for example id => friendlyname
@@ -71,20 +72,75 @@ abstract class TabularBulkExport extends BulkExport
 				$sAttCode = $sField;
 				$sClass = reset($aAliases);
 			}
-			$aSuggestedFields[$idx] = $this->SuggestField($aAliases, $sClass, $sAlias, $sAttCode);
+			$sMostRelevantField = $this->SuggestField($sClass, $sAttCode);
+			$sAttCodeEx = $this->NormalizeFieldSpec($sClass, $sMostRelevantField);
+			// Remove the aliases (if any) from the field names to make them compatible
+			// with the 'short' notation used in this case by the widget
+			if (count($aAliases) > 1)
+			{
+				$sAttCodeEx = $sAlias.'.'.$sAttCodeEx;
+			}
+			$aRet[] = $sAttCodeEx;
 		}
-		return $aSuggestedFields;
+		return $aRet;
+	}
+
+	protected function SuggestField($sClass, $sAttCode)
+	{
+		return $sAttCode;
 	}
 
-	protected function SuggestField($aAliases, $sClass, $sAlias, $sAttCode)
+	/**
+	 * Given a field spec, get the most relevant (unique) representation
+	 * Examples for a user request:
+	 * - friendlyname => ref
+	 * - org_name => org_id->name
+	 * - org_id_friendlyname => org_id=>name
+	 * - caller_name => caller_id->name
+	 * - caller_id_friendlyname => caller_id->friendlyname	 	 	 	 	 	 
+	 */	 	
+	protected function NormalizeFieldSpec($sClass, $sField)
 	{
-		// Remove the aliases (if any) from the field names to make them compatible
-		// with the 'short' notation used in this case by the widget
-		if (count($aAliases) == 1)
+		$sRet = $sField;
+
+		if ($sField == 'id')
 		{
-			return $sAttCode;
+			$sRet = 'id';
 		}
-		return $sAlias.'.'.$sAttCode;
+		elseif ($sField == 'friendlyname')
+		{
+			$sFriendlyNameAttCode = MetaModel::GetFriendlyNameAttributeCode($sClass);
+			if (!is_null($sFriendlyNameAttCode))
+			{
+				// The friendly name is made of a single attribute
+				$sRet = $sFriendlyNameAttCode;
+			}
+		}
+		else
+		{
+			$oAttDef = MetaModel::GetAttributeDef($sClass, $sField);
+			if ($oAttDef instanceof AttributeFriendlyName)
+			{
+				$oKeyAttDef = MetaModel::GetAttributeDef($sClass, $oAttDef->GetKeyAttCode());
+				$sRemoteClass = $oKeyAttDef->GetTargetClass();
+				$sFriendlyNameAttCode = MetaModel::GetFriendlyNameAttributeCode($sRemoteClass);
+				if (is_null($sFriendlyNameAttCode))
+				{
+					// The friendly name is made of several attributes
+					$sRet = $oAttDef->GetKeyAttCode().'->friendlyname';
+				}
+				else
+				{
+					// The friendly name is made of a single attribute
+					$sRet = $oAttDef->GetKeyAttCode().'->'.$sFriendlyNameAttCode;
+				}
+			}
+			elseif ($oAttDef->IsExternalField())
+			{
+				$sRet = $oAttDef->GetKeyAttCode().'->'.$oAttDef->GetExtAttCode();
+			}
+		}
+		return $sRet;
 	}
 
 	protected function IsSubAttribute($sClass, $sAttCode, $oAttDef)
@@ -99,19 +155,64 @@ abstract class TabularBulkExport extends BulkExport
 		{
 			case 'AttributeExternalKey':
 			case 'AttributeHierarchicalKey':
-				$aResult[] = array('code' => $sAttCode, 'unique_label' => $oAttDef->GetLabel(), 'label' => Dict::S('Core:BulkExport:Identifier'), 'attdef' => $oAttDef);
-					$aResult[] = array('code' =>  $sAttCode.'_friendlyname', 'unique_label' => $oAttDef->GetLabel().'->'.Dict::S('Core:FriendlyName-Label'), 'label' => Dict::S('Core:FriendlyName-Label'), 'attdef' => MetaModel::GetAttributeDef($sClass, $sAttCode.'_friendlyname'));
+
+				$bAddFriendlyName = true;
+				$oKeyAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
+				$sRemoteClass = $oKeyAttDef->GetTargetClass();
+				$sFriendlyNameAttCode = MetaModel::GetFriendlyNameAttributeCode($sRemoteClass);
+				if (!is_null($sFriendlyNameAttCode))
+				{
+					// The friendly name is made of a single attribute, check if that attribute is present as an external field
+					foreach(MetaModel::ListAttributeDefs($sClass) as $sSubAttCode => $oSubAttDef)
+					{
+						if ($oSubAttDef instanceof AttributeExternalField)
+						{
+							if (($oSubAttDef->GetKeyAttCode() == $sAttCode) && ($oSubAttDef->GetExtAttCode() == $sFriendlyNameAttCode))
+							{
+								$bAddFriendlyName = false;
+							}
+						}
+					}
+				}
+
+				$aResult[$sAttCode] = array('code' => $sAttCode, 'unique_label' => $oAttDef->GetLabel(), 'label' => Dict::S('UI:CSVImport:idField'), 'attdef' => $oAttDef);
+
+				if ($bAddFriendlyName)
+				{
+					if ($this->IsExportableField($sClass, $sAttCode.'->friendlyname'))
+					{
+						$aResult[$sAttCode.'->friendlyname'] = array('code' =>  $sAttCode.'->friendlyname', 'unique_label' => $oAttDef->GetLabel().'->'.Dict::S('Core:FriendlyName-Label'), 'label' => Dict::S('Core:FriendlyName-Label'), 'attdef' => MetaModel::GetAttributeDef($sClass, $sAttCode.'_friendlyname'));
+					}
+				}
 
 				foreach(MetaModel::ListAttributeDefs($sClass) as $sSubAttCode => $oSubAttDef)
 				{
 					if ($oSubAttDef instanceof AttributeExternalField)
 					{
-						if ($oSubAttDef->GetKeyAttCode() == $sAttCode)
+						if ($this->IsExportableField($sClass, $sSubAttCode, $oSubAttDef))
 						{
-							$aResult[] = array('code' => $sSubAttCode, 'unique_label' => $oAttDef->GetLabel().'->'.$oSubAttDef->GetExtAttDef()->GetLabel(), 'label' => $oSubAttDef->GetExtAttDef()->GetLabel(), 'attdef' => $oSubAttDef);
+							if ($oSubAttDef->GetKeyAttCode() == $sAttCode)
+							{
+								$sAttCodeEx = $sAttCode.'->'.$oSubAttDef->GetExtAttCode();
+								$aResult[$sAttCodeEx] = array('code' => $sAttCodeEx, 'unique_label' => $oAttDef->GetLabel().'->'.$oSubAttDef->GetExtAttDef()->GetLabel(), 'label' => MetaModel::GetLabel($sRemoteClass, $oSubAttDef->GetExtAttCode()), 'attdef' => $oSubAttDef);
+							}
 						}
 					}
 				}
+
+				// Add the reconciliation keys
+				foreach(MetaModel::GetReconcKeys($sRemoteClass) as $sRemoteAttCode)
+			  	{
+					$sAttCodeEx = $sAttCode.'->'.$sRemoteAttCode;
+					if (!array_key_exists($sAttCodeEx, $aResult))
+					{
+						$oRemoteAttDef = MetaModel::GetAttributeDef($sRemoteClass, $sRemoteAttCode);
+						if ($this->IsExportableField($sRemoteClass, $sRemoteAttCode, $oRemoteAttDef))
+						{
+							$aResult[$sAttCodeEx] = array('code' =>  $sAttCodeEx, 'unique_label' => $oAttDef->GetLabel().'->'.$oRemoteAttDef->GetLabel(), 'label' => MetaModel::GetLabel($sRemoteClass, $sRemoteAttCode), 'attdef' => $oRemoteAttDef);
+						}
+					}
+			  	}
 				break;
 					
 			case 'AttributeStopWatch':
@@ -121,12 +222,14 @@ abstract class TabularBulkExport extends BulkExport
 					{
 						if ($oSubAttDef->GetParentAttCode() == $sAttCode)
 						{
-							$aResult[] = array('code' => $sSubAttCode, 'unique_label' => $oSubAttDef->GetLabel(), 'label' => $oSubAttDef->GetLabel(), 'attdef' => $oSubAttDef);
+							if ($this->IsExportableField($sClass, $sSubAttCode, $oSubAttDef))
+							{
+								$aResult[$sSubAttCode] = array('code' => $sSubAttCode, 'unique_label' => $oSubAttDef->GetLabel(), 'label' => $oSubAttDef->GetLabel(), 'attdef' => $oSubAttDef);
+							}
 						}
 					}
 				}
 				break;
-					
 		}
 		return $aResult;
 	}
@@ -144,6 +247,7 @@ abstract class TabularBulkExport extends BulkExport
 			}
 		}
 		$aAllFieldsByAlias = array();
+		$aAllAttCodes = array();
 		foreach($aAuthorizedClasses as $sAlias => $sClass)
 		{
 			$aAllFields = array();
@@ -155,18 +259,29 @@ abstract class TabularBulkExport extends BulkExport
 			{
 				$sShortAlias = '';
 			}
-			if ($this->IsValidField($sClass, 'id'))
+			if ($this->IsExportableField($sClass, 'id'))
 			{
-				$aAllFields[] = array('code' =>  $sShortAlias.'id', 'unique_label' => $sShortAlias.Dict::S('Core:BulkExport:Identifier'), 'label' => $sShortAlias.'id', 'subattr' => array(
-						array('code' =>  $sShortAlias.'id', 'unique_label' => $sShortAlias.Dict::S('Core:BulkExport:Identifier'), 'label' => $sShortAlias.'id'),
-						array('code' =>  $sShortAlias.'friendlyname', 'unique_label' => $sShortAlias.Dict::S('Core:FriendlyName-Label'), 'label' => $sShortAlias.Dict::S('Core:FriendlyName-Label')),
-				));
+				$sFriendlyNameAttCode = MetaModel::GetFriendlyNameAttributeCode($sClass);
+				if (is_null($sFriendlyNameAttCode))
+				{
+					// The friendly name is made of several attribute
+					$aSubAttr = array(
+						array('attcodeex' => 'id', 'code' => $sShortAlias.'id', 'unique_label' => $sShortAlias.Dict::S('UI:CSVImport:idField'), 'label' => $sShortAlias.'id'),
+						array('attcodeex' => 'friendlyname', 'code' => $sShortAlias.'friendlyname', 'unique_label' => $sShortAlias.Dict::S('Core:FriendlyName-Label'), 'label' => $sShortAlias.Dict::S('Core:FriendlyName-Label')),
+					);
 				}
+				else
+				{
+					// The friendly name has no added value
+					$aSubAttr = array();
+				}
+				$aAllFields[] = array('attcodeex' => 'id', 'code' => $sShortAlias.'id', 'unique_label' => $sShortAlias.Dict::S('UI:CSVImport:idField'), 'label' => Dict::S('UI:CSVImport:idField'), 'subattr' => $aSubAttr);
+			}
 			foreach(MetaModel::ListAttributeDefs($sClass) as $sAttCode => $oAttDef)
 			{
 				if($this->IsSubAttribute($sClass, $sAttCode, $oAttDef)) continue;
 
-				if ($this->IsValidField($sClass, $sAttCode, $oAttDef))
+				if ($this->IsExportableField($sClass, $sAttCode, $oAttDef))
 				{
 					$sShortLabel = $oAttDef->GetLabel();
 					$sLabel = $sShortAlias.$oAttDef->GetLabel();
@@ -174,12 +289,9 @@ abstract class TabularBulkExport extends BulkExport
 					$aValidSubAttr = array();
 					foreach($aSubAttr as $aSubAttDef)
 					{
-						if ($this->IsValidField($sClass, $aSubAttDef['code'], $aSubAttDef['attdef']))
-						{
-							$aValidSubAttr[] = array('code' => $sShortAlias.$aSubAttDef['code'], 'label' => $aSubAttDef['label'], 'unique_label' => $aSubAttDef['unique_label']);
-						}
+						$aValidSubAttr[] = array('attcodeex' => $aSubAttDef['code'], 'code' => $sShortAlias.$aSubAttDef['code'], 'label' => $aSubAttDef['label'], 'unique_label' => $sShortAlias.$aSubAttDef['unique_label']);
 					}
-					$aAllFields[] = array('code' => $sShortAlias.$sAttCode, 'label' => $sShortLabel, 'unique_label' => $sLabel, 'subattr' => $aValidSubAttr);
+					$aAllFields[] = array('attcodeex' => $sAttCode, 'code' => $sShortAlias.$sAttCode, 'label' => $sShortLabel, 'unique_label' => $sLabel, 'subattr' => $aValidSubAttr);
 				}
 			}
 			usort($aAllFields,  array(get_class($this), 'SortOnLabel'));
@@ -192,12 +304,36 @@ abstract class TabularBulkExport extends BulkExport
 				$sKey = MetaModel::GetName($sClass);
 			}
 			$aAllFieldsByAlias[$sKey] = $aAllFields;
+
+			foreach ($aAllFields as $aFieldSpec)
+			{
+				$sAttCode = $aFieldSpec['attcodeex'];
+				if (count($aFieldSpec['subattr']) > 0)
+				{
+					foreach ($aFieldSpec['subattr'] as $aSubFieldSpec)
+					{
+						$aAllAttCodes[$sAlias][] = $aSubFieldSpec['attcodeex'];
+					}
+				}
+				else
+				{
+					$aAllAttCodes[$sAlias][] = $sAttCode;
+				}
+			}
 		}
 
 		$oP->add('<div id="'.$sWidgetId.'"></div>');
 		$JSAllFields = json_encode($aAllFieldsByAlias);
+
+		// First, fetch only the ids - the rest will be fetched by an object reload
 		$oSet = new DBObjectSet($this->oSearch);
 		$iCount = $oSet->Count();
+
+		foreach ($this->oSearch->GetSelectedClasses() as $sAlias => $sClass)
+		{
+			$aColumns[$sAlias] = array();
+		}
+		$oSet->OptimizeColumnLoad($aColumns);
 		$iPreviewLimit = 3;
 		$oSet->SetLimit($iPreviewLimit);
 		$aSampleData = array();
@@ -215,16 +351,10 @@ abstract class TabularBulkExport extends BulkExport
 					$sShortAlias = '';
 				}
 
-				if ($this->IsValidField($sClass, 'id'))
-				{
-					$aSampleRow[$sShortAlias.'id'] = $this->GetSampleKey($aRow[$sAlias]);
-				}
-				foreach(MetaModel::ListAttributeDefs($sClass) as $sAttCode => $oAttDef)
+				foreach ($aAllAttCodes[$sAlias] as $sAttCodeEx)
 				{
-					if ($this->IsValidField($sClass, $sAttCode, $oAttDef))
-					{
-						$aSampleRow[$sShortAlias.$sAttCode] = $this->GetSampleData($aRow[$sAlias], $sAttCode);
-					}
+					$oObj = $aRow[$sAlias];
+					$aSampleRow[$sShortAlias.$sAttCodeEx] = $oObj ? $this->GetSampleData($oObj, $sAttCodeEx) : '';
 				}
 			}
 			$aSampleData[] = $aSampleRow;
@@ -256,28 +386,26 @@ EOF
 	 * Tells if the specified field can be exported
 	 * @param unknown $sClass
 	 * @param unknown $sAttCode
-	 * @param AttributeDefinition $oAttDef Can be null when $sAttCode == 'id'
+	 * @param AttributeDefinition $oAttDef Can be null in case the attribute definition has not been fetched by the caller
 	 * @return boolean
 	 */
-	protected function IsValidField($sClass, $sAttCode, $oAttDef = null)
+	protected function IsExportableField($sClass, $sAttCode, $oAttDef = null)
 	{
 		if ($sAttCode == 'id') return true;
+		if (is_null($oAttDef))
+		{
+			$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
+		}
 		if ($oAttDef instanceof AttributeLinkedSet) return false;
 		return true; //$oAttDef->IsScalar();
 	}
 
 	protected function GetSampleData($oObj, $sAttCode)
 	{
-		if ($oObj == null) return '';
+		if ($sAttCode == 'id') return $oObj->GetKey();
 		return $oObj->GetEditValue($sAttCode);
 	}
 
-	protected function GetSampleKey($oObj)
-	{
-		if ($oObj == null) return '';
-		return $oObj->GetKey();
-	}
-
 	public function ReadParameters()
 	{
 		parent::ReadParameters();
@@ -358,7 +486,7 @@ EOF
 				catch (Exception $e)
 				{
 					throw new Exception("Wrong field specification '$sFieldSpec': ".$e->getMessage());
-				}
+			}
 			}
 			else
 			{

+ 1 - 8
core/xmlbulkexport.class.inc.php

@@ -70,14 +70,7 @@ class XMLBulkExport extends BulkExport
 	
 	protected function GetSampleData($oObj, $sAttCode)
 	{
-		if ($oObj)
-		{
-			$sRet = $oObj->GetAsXML($sAttCode);
-		}
-		else
-		{
-			$sRet = '';
-		}
+		$sRet = ($sAttCode == 'id') ? $oObj->GetKey() : $oObj->GetAsXML($sAttCode);
 		return $sRet;
 	}
 

+ 13 - 0
css/light-grey.css

@@ -1956,6 +1956,9 @@ table.export_parameters td {
 
 .table_preview > table {
   border-collapse: collapse;
+  max-height: 150px;
+  overflow: auto;
+  display: block;
 }
 
 
@@ -1968,11 +1971,21 @@ table.export_parameters td {
 }
 
 
+.table_preview > table > tbody > tr > td {
+  vertical-align: top;
+}
+
+
 .table_preview .drag-handle {
   cursor: move;
 }
 
 
+.table_preview div.text-preview {
+  white-space: pre-wrap;
+}
+
+
 .graph_zoom {
   display: inline-block;
   float: right;

+ 10 - 0
css/light-grey.scss

@@ -1445,8 +1445,12 @@ div.ui-dialog-header {
 table.export_parameters td {
 	padding-right: 2em;
 }
+
 .table_preview > table {
 	border-collapse: collapse;
+	max-height: 150px;
+	overflow: auto;
+	display: block;
 }
 .table_preview > table > thead > tr > th, .table_preview > table > tbody > tr > td {
 	border: 1px $grey-color solid;
@@ -1455,9 +1459,15 @@ table.export_parameters td {
 	padding-right: 0.25em;
 	font-size: 10pt;
 }
+.table_preview > table > tbody > tr > td {
+	vertical-align: top;
+}
 .table_preview .drag-handle {
 	cursor: move;	
 }
+.table_preview div.text-preview {
+	white-space: pre-wrap;
+}
 .graph_zoom {
 	display: inline-block;
 	float: right;

+ 0 - 1
dictionaries/da.dictionary.itop.core.php

@@ -2446,7 +2446,6 @@ Operators:<br/>
 	'Core:BulkExport:MissingParameter_Param' => 'Missing parameter \"%1$s\"~~',
 	'Core:BulkExport:InvalidParameter_Query' => 'Invalid value for the parameter \"query\". There is no Query Phrasebook corresponding to the id: \"%1$s\".~~',
 	'Core:BulkExport:ExportFormatPrompt' => 'Export format:~~',
-	'Core:BulkExport:Identifier' => 'Identifier~~',
 	'Core:BulkExportOf_Class' => '%1$s Export~~',
 	'Core:BulkExport:ClickHereToDownload_FileName' => 'Click here to download %1$s~~',
 	'Core:BulkExport:ExportResult' => 'Result of the export:~~',

+ 0 - 1
dictionaries/de.dictionary.itop.core.php

@@ -558,7 +558,6 @@ Operatoren:<br/>
 	'Core:BulkExport:MissingParameter_Param' => 'Fehlender Parameter "%1$s"',
 	'Core:BulkExport:InvalidParameter_Query' => 'ungültiger Wert für den Paramter "query". Es gibt keinen Eintrag in der Query-Bibliothek, der zu der id "%1$s" korrespondiert.',
 	'Core:BulkExport:ExportFormatPrompt' => 'Exportformat:',
-	'Core:BulkExport:Identifier' => 'Identifikator',
 	'Core:BulkExportOf_Class' => '%1$s-Export',
 	'Core:BulkExport:ClickHereToDownload_FileName' => 'Klicken Sie hier um %1$s herunterzuladen',
 	'Core:BulkExport:ExportResult' => 'Ergebnis ses Exportvorgangs:',

+ 0 - 1
dictionaries/dictionary.itop.core.php

@@ -810,7 +810,6 @@ Dict::Add('EN US', 'English', 'English', array(
 	'Core:BulkExport:MissingParameter_Param' => 'Missing parameter "%1$s"',
 	'Core:BulkExport:InvalidParameter_Query' => 'Invalid value for the parameter "query". There is no Query Phrasebook corresponding to the id: "%1$s".',
 	'Core:BulkExport:ExportFormatPrompt' => 'Export format:',
-	'Core:BulkExport:Identifier' => 'Identifier',
 	'Core:BulkExportOf_Class' => '%1$s Export',
 	'Core:BulkExport:ClickHereToDownload_FileName' => 'Click here to download %1$s',
 	'Core:BulkExport:ExportResult' => 'Result of the export:',

+ 0 - 1
dictionaries/es_cr.dictionary.itop.core.php

@@ -804,7 +804,6 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
 	'Core:BulkExport:MissingParameter_Param' => 'Missing parameter \"%1$s\"~~',
 	'Core:BulkExport:InvalidParameter_Query' => 'Invalid value for the parameter \"query\". There is no Query Phrasebook corresponding to the id: \"%1$s\".~~',
 	'Core:BulkExport:ExportFormatPrompt' => 'Export format:~~',
-	'Core:BulkExport:Identifier' => 'Identifier~~',
 	'Core:BulkExportOf_Class' => '%1$s Export~~',
 	'Core:BulkExport:ClickHereToDownload_FileName' => 'Click here to download %1$s~~',
 	'Core:BulkExport:ExportResult' => 'Result of the export:~~',

+ 0 - 1
dictionaries/fr.dictionary.itop.core.php

@@ -667,7 +667,6 @@ Opérateurs :<br/>
 	'Core:BulkExport:MissingParameter_Param' => 'Il manque le paramètre "%1$s"',
 	'Core:BulkExport:InvalidParameter_Query' => 'Valeur incorrecte pour le paramètre "query". Il n\'existe aucune entrée dans le livre des requêtes pour l\'identifiant: "%1$s"',
 	'Core:BulkExport:ExportFormatPrompt' => 'Format d\'export:',
-	'Core:BulkExport:Identifier' => 'Identifiant',
 	'Core:BulkExportOf_Class' => 'Export de: %1$s',
 	'Core:BulkExport:ClickHereToDownload_FileName' => 'Cliquez ici pour télécharger %1$s',
 	'Core:BulkExport:ExportResult' => 'Résultat de l\'export:',

+ 0 - 1
dictionaries/hu.dictionary.itop.core.php

@@ -561,7 +561,6 @@ Operators:<br/>
 	'Core:BulkExport:MissingParameter_Param' => 'Missing parameter \"%1$s\"~~',
 	'Core:BulkExport:InvalidParameter_Query' => 'Invalid value for the parameter \"query\". There is no Query Phrasebook corresponding to the id: \"%1$s\".~~',
 	'Core:BulkExport:ExportFormatPrompt' => 'Export format:~~',
-	'Core:BulkExport:Identifier' => 'Identifier~~',
 	'Core:BulkExportOf_Class' => '%1$s Export~~',
 	'Core:BulkExport:ClickHereToDownload_FileName' => 'Click here to download %1$s~~',
 	'Core:BulkExport:ExportResult' => 'Result of the export:~~',

+ 0 - 1
dictionaries/it.dictionary.itop.core.php

@@ -793,7 +793,6 @@ Dict::Add('IT IT', 'Italian', 'Italiano', array(
 	'Core:BulkExport:MissingParameter_Param' => 'Missing parameter \"%1$s\"~~',
 	'Core:BulkExport:InvalidParameter_Query' => 'Invalid value for the parameter \"query\". There is no Query Phrasebook corresponding to the id: \"%1$s\".~~',
 	'Core:BulkExport:ExportFormatPrompt' => 'Export format:~~',
-	'Core:BulkExport:Identifier' => 'Identifier~~',
 	'Core:BulkExportOf_Class' => '%1$s Export~~',
 	'Core:BulkExport:ClickHereToDownload_FileName' => 'Click here to download %1$s~~',
 	'Core:BulkExport:ExportResult' => 'Result of the export:~~',

+ 0 - 1
dictionaries/ja.dictionary.itop.core.php

@@ -583,7 +583,6 @@ Operators:<br/>
 	'Core:BulkExport:MissingParameter_Param' => 'Missing parameter \"%1$s\"~~',
 	'Core:BulkExport:InvalidParameter_Query' => 'Invalid value for the parameter \"query\". There is no Query Phrasebook corresponding to the id: \"%1$s\".~~',
 	'Core:BulkExport:ExportFormatPrompt' => 'Export format:~~',
-	'Core:BulkExport:Identifier' => 'Identifier~~',
 	'Core:BulkExportOf_Class' => '%1$s Export~~',
 	'Core:BulkExport:ClickHereToDownload_FileName' => 'Click here to download %1$s~~',
 	'Core:BulkExport:ExportResult' => 'Result of the export:~~',

+ 0 - 1
dictionaries/nl.dictionary.itop.core.php

@@ -813,7 +813,6 @@ Dict::Add('NL NL', 'Dutch', 'Nederlands', array(
 	'Core:BulkExport:MissingParameter_Param' => 'Missing parameter \"%1$s\"~~',
 	'Core:BulkExport:InvalidParameter_Query' => 'Invalid value for the parameter \"query\". There is no Query Phrasebook corresponding to the id: \"%1$s\".~~',
 	'Core:BulkExport:ExportFormatPrompt' => 'Export format:~~',
-	'Core:BulkExport:Identifier' => 'Identifier~~',
 	'Core:BulkExportOf_Class' => '%1$s Export~~',
 	'Core:BulkExport:ClickHereToDownload_FileName' => 'Click here to download %1$s~~',
 	'Core:BulkExport:ExportResult' => 'Result of the export:~~',

+ 0 - 1
dictionaries/pt_br.dictionary.itop.core.php

@@ -806,7 +806,6 @@ Dict::Add('PT BR', 'Brazilian', 'Brazilian', array(
 	'Core:BulkExport:MissingParameter_Param' => 'Missing parameter \"%1$s\"~~',
 	'Core:BulkExport:InvalidParameter_Query' => 'Invalid value for the parameter \"query\". There is no Query Phrasebook corresponding to the id: \"%1$s\".~~',
 	'Core:BulkExport:ExportFormatPrompt' => 'Export format:~~',
-	'Core:BulkExport:Identifier' => 'Identifier~~',
 	'Core:BulkExportOf_Class' => '%1$s Export~~',
 	'Core:BulkExport:ClickHereToDownload_FileName' => 'Click here to download %1$s~~',
 	'Core:BulkExport:ExportResult' => 'Result of the export:~~',

+ 0 - 1
dictionaries/ru.dictionary.itop.core.php

@@ -801,7 +801,6 @@ Dict::Add('RU RU', 'Russian', 'Русский', array(
 	'Core:BulkExport:MissingParameter_Param' => 'Missing parameter \"%1$s\"~~',
 	'Core:BulkExport:InvalidParameter_Query' => 'Invalid value for the parameter \"query\". There is no Query Phrasebook corresponding to the id: \"%1$s\".~~',
 	'Core:BulkExport:ExportFormatPrompt' => 'Export format:~~',
-	'Core:BulkExport:Identifier' => 'Identifier~~',
 	'Core:BulkExportOf_Class' => '%1$s Export~~',
 	'Core:BulkExport:ClickHereToDownload_FileName' => 'Click here to download %1$s~~',
 	'Core:BulkExport:ExportResult' => 'Result of the export:~~',

+ 0 - 1
dictionaries/tr.dictionary.itop.core.php

@@ -733,7 +733,6 @@ Operators:<br/>
 	'Core:BulkExport:MissingParameter_Param' => 'Missing parameter \"%1$s\"~~',
 	'Core:BulkExport:InvalidParameter_Query' => 'Invalid value for the parameter \"query\". There is no Query Phrasebook corresponding to the id: \"%1$s\".~~',
 	'Core:BulkExport:ExportFormatPrompt' => 'Export format:~~',
-	'Core:BulkExport:Identifier' => 'Identifier~~',
 	'Core:BulkExportOf_Class' => '%1$s Export~~',
 	'Core:BulkExport:ClickHereToDownload_FileName' => 'Click here to download %1$s~~',
 	'Core:BulkExport:ExportResult' => 'Result of the export:~~',

+ 0 - 1
dictionaries/zh.dictionary.itop.core.php

@@ -732,7 +732,6 @@ Operators:<br/>
 	'Core:BulkExport:MissingParameter_Param' => 'Missing parameter \"%1$s\"~~',
 	'Core:BulkExport:InvalidParameter_Query' => 'Invalid value for the parameter \"query\". There is no Query Phrasebook corresponding to the id: \"%1$s\".~~',
 	'Core:BulkExport:ExportFormatPrompt' => 'Export format:~~',
-	'Core:BulkExport:Identifier' => 'Identifier~~',
 	'Core:BulkExportOf_Class' => '%1$s Export~~',
 	'Core:BulkExport:ClickHereToDownload_FileName' => 'Click here to download %1$s~~',
 	'Core:BulkExport:ExportResult' => 'Result of the export:~~',

+ 1 - 0
webservices/export-v2.php

@@ -489,6 +489,7 @@ function CheckParameters($sExpression, $sQueryId, $sFormat)
 
 function DoExport(Page $oP, BulkExport $oExporter, $bInteractive = false)
 {
+	$oExporter->SetHttpHeaders($oP);
 	$exportResult = $oExporter->GetHeader();
 	$aStatus = array();
 	do

+ 23 - 6
webservices/itoprest.examples.php

@@ -185,10 +185,15 @@ $aOperations = array(
 );
 $aOperations = array(
 	array(
-		'operation' => 'core/get', // operation code
-		'class' => 'PhysicalDevice',
-		'key' => 'SELECT PhysicalDevice WHERE id < 3',
-		'output_fields' => '*+', // list of fields to show in the results (* or a,b,c)
+		'operation' => 'core/update', // operation code
+		'comment' => 'Synchronization from blah...', // comment recorded in the change tracking log
+		'class' => 'Server',
+		'key' => 'SELECT Server WHERE name="Server1"',
+		'output_fields' => 'id, friendlyname, description', // list of fields to show in the results (* or a,b,c)
+		// Values for the object to create
+		'fields' => array(
+			'description' => 'Issue #'.time(),
+		),
 	),
 );
 $aOperations = array(
@@ -213,6 +218,15 @@ $aXXXOperations = array(
 		'password' => 'admin',
 	),
 );
+$aOperations = array(
+	array(
+		'operation' => 'core/delete', // operation code
+		'comment' => 'Cleanup for synchro with...', // comment recorded in the change tracking log
+		'class' => 'Server',
+		'key' => 'SELECT Server',
+		'simulate' => false,
+	),
+);
 
 if (false)
 {
@@ -225,8 +239,11 @@ else
 }
 
 $aData = array();
-$aData['auth_user'] = 'admin';
-$aData['auth_pwd'] = 'admin';
+$aData['auth_user'] = 'no-export';
+$aData['auth_pwd'] = 'no-export';
+//$aData['auth_user'] = 'admin';
+//$aData['auth_pwd'] = 'admin';
+
 
 foreach ($aOperations as $iOp => $aOperation)
 {