瀏覽代碼

Export (all formats but XML):
- code refactoring
- suppressed the '*' for mandatory ext keys (buggy anyway)
- fixed issue with redundant columns (resulting in a badly formatted export)
- check that the current user has the rights to "bulk read" the selected objects (that depend on the selected fields)

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

romainq 9 年之前
父節點
當前提交
8ddc97daa3

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

@@ -133,6 +133,7 @@ abstract class BulkExport
 	protected $aStatusInfo;
 	protected $oBulkExportResult;
 	protected $sTmpFile;
+	protected $bLocalizeOutput;
 	
 	public function __construct()
 	{
@@ -142,6 +143,7 @@ abstract class BulkExport
 		$this->aStatusInfo = array();
 		$this->oBulkExportResult = null;
 		$this->sTmpFile = '';
+		$this->bLocalizeOutput = false;
 	}
 		
 	/**
@@ -331,7 +333,7 @@ abstract class BulkExport
 	}
 	public function ReadParameters()
 	{
-		
+		$this->bLocalizeOutput = !((bool)utils::ReadParam('no_localize', 0, true, 'integer'));
 	}
 	
 	public function GetResultAsHtml()

+ 11 - 112
core/csvbulkexport.class.inc.php

@@ -53,7 +53,7 @@ class CSVBulkExport extends TabularBulkExport
 		{
 			$this->aStatusInfo['text_qualifier'] = utils::ReadParam('other-text-qualifier', '"', true, 'raw_data');
 		}
-		$this->aStatusInfo['localize'] = !((bool)utils::ReadParam('no_localize', 0, true, 'integer'));
+
 		$this->aStatusInfo['charset'] = strtoupper(utils::ReadParam('charset', 'UTF-8', true, 'raw_data'));
 	}
 
@@ -188,70 +188,10 @@ class CSVBulkExport extends TabularBulkExport
 		$this->aStatusInfo['position'] = 0;
 		$this->aStatusInfo['total'] = $oSet->Count();
 
-		$aSelectedClasses = $this->oSearch->GetSelectedClasses();
-		$aAuthorizedClasses = array();
-		foreach($aSelectedClasses as $sAlias => $sClassName)
-		{
-			if (UserRights::IsActionAllowed($sClassName, UR_ACTION_BULK_READ, $oSet) && (UR_ALLOWED_YES || UR_ALLOWED_DEPENDS))
-			{
-				$aAuthorizedClasses[$sAlias] = $sClassName;
-			}
-		}
-		$aAliases = array_keys($aAuthorizedClasses);
 		$aData = array();
-		foreach($this->aStatusInfo['fields'] as $sExtendedAttCode)
+		foreach($this->aStatusInfo['fields'] as $iCol => $aFieldSpec)
 		{
-			if (preg_match('/^([^\.]+)\.(.+)$/', $sExtendedAttCode, $aMatches))
-			{
-				$sAlias = $aMatches[1];
-				$sAttCode = $aMatches[2];
-			}
-			else
-			{
-				$sAlias = reset($aAliases);
-				$sAttCode = $sExtendedAttCode;
-			}
-			if (!in_array($sAlias, $aAliases))
-			{
-				throw new Exception("Invalid alias '$sAlias' for the column '$sExtendedAttCode'. Availables aliases: '".implode("', '", $aAliases)."'");
-			}
-			$sClass = $aAuthorizedClasses[$sAlias];
-				
-			switch($sAttCode)
-			{
-				case 'id':
-				$sLabel = 'id';
-				break;
-						
-				default:
-				$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
-				if (($oAttDef instanceof AttributeExternalField) || (($oAttDef instanceof AttributeFriendlyName) && ($oAttDef->GetKeyAttCode() != 'id')))
-				{
-					$oKeyAttDef = MetaModel::GetAttributeDef($sClass, $oAttDef->GetKeyAttCode());
-					$oExtAttDef = MetaModel::GetAttributeDef($oKeyAttDef->GetTargetClass(), $oAttDef->GetExtAttCode());
-					if ($this->aStatusInfo['localize'])
-					{
-						$sStar = $oAttDef->IsNullAllowed() ? '' : '*';
-						$sLabel = $oKeyAttDef->GetLabel().$sStar.'->'.$oExtAttDef->GetLabel();
-					}
-					else
-					{
-						$sLabel =  $oKeyAttDef->GetCode().'->'.$oExtAttDef->GetCode();
-					}						
-				}
-				else
-				{
-					$sLabel = $this->aStatusInfo['localize'] ? $oAttDef->GetLabel() : $sAttCode;
-				}
-			}
-			if (count($aAuthorizedClasses) > 1)
-			{
-				$aData[] = $sAlias.'.'.$sLabel;
-			}
-			else
-			{
-				$aData[] = $sLabel;
-			}
+			$aData[] = $aFieldSpec['sColLabel'];
 		}
 		$sFrom = array("\r\n", $this->aStatusInfo['text_qualifier']);
 		$sTo = array("\n", $this->aStatusInfo['text_qualifier'].$this->aStatusInfo['text_qualifier']);
@@ -278,75 +218,34 @@ class CSVBulkExport extends TabularBulkExport
 		$iPercentage = 0;
 
 		$oSet = new DBObjectSet($this->oSearch);
-		$aSelectedClasses = $this->oSearch->GetSelectedClasses();
-		$aAuthorizedClasses = array();
-		foreach($aSelectedClasses as $sAlias => $sClassName)
-		{
-			if (UserRights::IsActionAllowed($sClassName, UR_ACTION_BULK_READ, $oSet) && (UR_ALLOWED_YES || UR_ALLOWED_DEPENDS))
-			{
-				$aAuthorizedClasses[$sAlias] = $sClassName;
-			}
-		}
-		$aAliases = array_keys($aAuthorizedClasses);
 		$oSet->SetLimit($this->iChunkSize, $this->aStatusInfo['position']);
-
-		$aAliasByField = array();
-		$aColumnsToLoad = array();
-
-		// Prepare the list of aliases / columns to load
-		foreach($this->aStatusInfo['fields'] as $sExtendedAttCode)
-		{
-			if (preg_match('/^([^\.]+)\.(.+)$/', $sExtendedAttCode, $aMatches))
-			{
-				$sAlias = $aMatches[1];
-				$sAttCode = $aMatches[2];
-			}
-			else
-			{
-				$sAlias = reset($aAliases);
-				$sAttCode = $sExtendedAttCode;
-			}
-				
-			if (!in_array($sAlias, $aAliases))
-			{
-				throw new Exception("Invalid alias '$sAlias' for the column '$sExtendedAttCode'. Availables aliases: '".implode("', '", $aAliases)."'");
-			}
-				
-			if (!array_key_exists($sAlias, $aColumnsToLoad))
-			{
-				$aColumnsToLoad[$sAlias] = array();
-			}
-			if ($sAttCode != 'id')
-			{
-				// id is not a real attribute code and, moreover, is always loaded
-				$aColumnsToLoad[$sAlias][] = $sAttCode;
-			}
-			$aAliasByField[$sExtendedAttCode] = array('alias' => $sAlias, 'attcode' => $sAttCode);
-		}
+		$this->OptimizeColumnLoad($oSet);
 
 		$iCount = 0;
 		$sData = '';
-		$oSet->OptimizeColumnLoad($aColumnsToLoad);
 		$iPreviousTimeLimit = ini_get('max_execution_time');
 		$iLoopTimeLimit = MetaModel::GetConfig()->Get('max_execution_time_per_loop');
 		while($aRow = $oSet->FetchAssoc())
 		{
 			set_time_limit($iLoopTimeLimit);
 			$aData = array();
-			foreach($aAliasByField as $aAttCode)
+			foreach($this->aStatusInfo['fields'] as $iCol => $aFieldSpec)
 			{
+				$sAlias = $aFieldSpec['sAlias'];
+				$sAttCode = $aFieldSpec['sAttCode'];
+
 				$sField = '';
-				$oObj = $aRow[$aAttCode['alias']];
+				$oObj = $aRow[$sAlias];
 				if ($oObj != null)
 				{
-					switch($aAttCode['attcode'])
+					switch($sAttCode)
 					{
 						case 'id':
 							$sField = $oObj->GetKey();
 							break;
 								
 						default:
-							$sField = $oObj->GetAsCSV($aAttCode['attcode'], $this->aStatusInfo['separator'], $this->aStatusInfo['text_qualifier'], $this->aStatusInfo['localize']);
+							$sField = $oObj->GetAsCSV($sAttCode, $this->aStatusInfo['separator'], $this->aStatusInfo['text_qualifier'], $this->bLocalizeOutput);
 					}
 				}
 				if ($this->aStatusInfo['charset'] != 'UTF-8')

+ 18 - 104
core/excelbulkexport.class.inc.php

@@ -67,13 +67,6 @@ class ExcelBulkExport extends TabularBulkExport
 		}
 	}
 
-	public function ReadParameters()
-	{
-		parent::ReadParameters();
-		$this->aStatusInfo['localize'] = !((bool)utils::ReadParam('no_localize', 0, true, 'integer'));
-	}
-
-
 	protected function SuggestField($aAliases, $sClass, $sAlias, $sAttCode)
 	{
 		switch($sAttCode)
@@ -101,74 +94,27 @@ class ExcelBulkExport extends TabularBulkExport
 		$this->aStatusInfo['position'] = 0;
 		$this->aStatusInfo['total'] = $oSet->Count();
 
-		$aSelectedClasses = $this->oSearch->GetSelectedClasses();
-		foreach($aSelectedClasses as $sAlias => $sClassName)
-		{
-			if (UserRights::IsActionAllowed($sClassName, UR_ACTION_BULK_READ, $oSet) && (UR_ALLOWED_YES || UR_ALLOWED_DEPENDS))
-			{
-				$aAuthorizedClasses[$sAlias] = $sClassName;
-			}
-		}
-		$aAliases = array_keys($aAuthorizedClasses);
-		$aTableHeaders = array();
-		foreach($this->aStatusInfo['fields'] as $sExtendedAttCode)
+		foreach($this->aStatusInfo['fields'] as $iCol => $aFieldSpec)
 		{
-			if (preg_match('/^([^\.]+)\.(.+)$/', $sExtendedAttCode, $aMatches))
-			{
-				$sAlias = $aMatches[1];
-				$sAttCode = $aMatches[2];
-			}
-			else
-			{
-				$sAlias = reset($aAliases);
-				$sAttCode = $sExtendedAttCode;
-			}
-			if (!in_array($sAlias, $aAliases))
-			{
-				throw new Exception("Invalid alias '$sAlias' for the column '$sExtendedAttCode'. Availables aliases: '".implode("', '", $aAliases)."'");
-			}
-			$sClass = $aSelectedClasses[$sAlias];
-
-			$sFullAlias = '';
-			if (count($aSelectedClasses) > 1)
-			{
-				$sFullAlias = $sAlias.'.';
-			}
+			$sExtendedAttCode = $aFieldSpec['sFieldSpec'];
+			$sAttCode = $aFieldSpec['sAttCode'];
+			$sColLabel = $aFieldSpec['sColLabel'];
 				
 			switch($sAttCode)
 			{
 				case 'id':
-					$aTableHeaders[] = array('label' => $sFullAlias.'id', 'type' => '0');
-
+					$sType = '0';
 					break;
 
 				default:
-					$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
+					$oAttDef = MetaModel::GetAttributeDef($aFieldSpec['sClass'], $aFieldSpec['sAttCode']);
 					$sType = 'string';
 					if($oAttDef instanceof AttributeDateTime)
 					{
 						$sType = 'datetime';
 					}
-					if (($oAttDef instanceof AttributeExternalField) || (($oAttDef instanceof AttributeFriendlyName) && ($oAttDef->GetKeyAttCode() != 'id')))
-					{
-						$oKeyAttDef = MetaModel::GetAttributeDef($sClass, $oAttDef->GetKeyAttCode());
-						$oExtAttDef = MetaModel::GetAttributeDef($oKeyAttDef->GetTargetClass(), $oAttDef->GetExtAttCode());
-						if ($this->aStatusInfo['localize'])
-						{
-							$sLabel =  $oKeyAttDef->GetLabel().'->'.$oExtAttDef->GetLabel();
-						}
-						else
-						{
-							$sLabel =  $oKeyAttDef->GetCode().'->'.$oExtAttDef->GetCode();
-						}
-					}
-					else
-					{
-						$sLabel = $this->aStatusInfo['localize'] ? $oAttDef->GetLabel() : $sAttCode;
-					}
-						
-					$aTableHeaders[] = array('label' => $sFullAlias.$sLabel, 'type' => $sType);
 			}
+			$aTableHeaders[] = array('label' => $sColLabel, 'type' => $sType);
 		}
 
 		$sRow = json_encode($aTableHeaders);
@@ -188,67 +134,35 @@ class ExcelBulkExport extends TabularBulkExport
 		$iPercentage = 0;
 
 		$hFile = fopen($this->aStatusInfo['tmp_file'], 'ab');
+
 		$oSet = new DBObjectSet($this->oSearch);
-		$aSelectedClasses = $this->oSearch->GetSelectedClasses();
-		$aAliases = array_keys($aSelectedClasses);
 		$oSet->SetLimit($this->iChunkSize, $this->aStatusInfo['position']);
-
-		$aAliasByField = array();
-		$aColumnsToLoad = array();
-
-		// Prepare the list of aliases / columns to load
-		foreach($this->aStatusInfo['fields'] as $sExtendedAttCode)
-		{
-			if (preg_match('/^([^\.]+)\.(.+)$/', $sExtendedAttCode, $aMatches))
-			{
-				$sAlias = $aMatches[1];
-				$sAttCode = $aMatches[2];
-			}
-			else
-			{
-				$sAlias = reset($aAliases);
-				$sAttCode = $sExtendedAttCode;
-			}
-
-			if (!in_array($sAlias, $aAliases))
-			{
-				throw new Exception("Invalid alias '$sAlias' for the column '$sExtendedAttCode'. Availables aliases: '".implode("', '", $aAliases)."'");
-			}
-
-			if (!array_key_exists($sAlias, $aColumnsToLoad))
-			{
-				$aColumnsToLoad[$sAlias] = array();
-			}
-			if ($sAttCode != 'id')
-			{
-				// id is not a real attribute code and, moreover, is always loaded
-				$aColumnsToLoad[$sAlias][] = $sAttCode;
-			}
-			$aAliasByField[$sExtendedAttCode] = array('alias' => $sAlias, 'attcode' => $sAttCode);
-		}
+		$this->OptimizeColumnLoad($oSet);
 
 		$iCount = 0;
-		$oSet->OptimizeColumnLoad($aColumnsToLoad);
 		$iPreviousTimeLimit = ini_get('max_execution_time');
 		$iLoopTimeLimit = MetaModel::GetConfig()->Get('max_execution_time_per_loop');
 		while($aRow = $oSet->FetchAssoc())
 		{
 			set_time_limit($iLoopTimeLimit);
 			$aData = array();
-			foreach($aAliasByField as $aAttCode)
+			foreach($this->aStatusInfo['fields'] as $iCol => $aFieldSpec)
 			{
-				$oObj = $aRow[$aAttCode['alias']];
+				$sAlias = $aFieldSpec['sAlias'];
+				$sAttCode = $aFieldSpec['sAttCode'];
+
+				$oObj = $aRow[$sAlias];
 				$sField = '';
 				if ($oObj)
 				{
-					switch($aAttCode['attcode'])
+					switch($sAttCode)
 					{
 						case 'id':
 							$sField = $oObj->GetKey();
 							break;
 								
 						default:
-						$value = $oObj->Get($aAttCode['attcode']);
+						$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!
@@ -256,12 +170,12 @@ class ExcelBulkExport extends TabularBulkExport
 						}
 						else if ($value instanceOf DBObjectSet)
 						{
-							$oAttDef = MetaModel::GetAttributeDef(get_class($oObj), $aAttCode['attcode']);
+							$oAttDef = MetaModel::GetAttributeDef(get_class($oObj), $sAttCode);
 							$sField =  $oAttDef->GetAsCSV($value, '', '', $oObj);
 						}
 						else
 						{
-							$oAttDef = MetaModel::GetAttributeDef(get_class($oObj), $aAttCode['attcode']);
+							$oAttDef = MetaModel::GetAttributeDef(get_class($oObj), $sAttCode);
 							$sField =  $oAttDef->GetEditValue($value, $oObj);
 						}
 					}

+ 13 - 109
core/htmlbulkexport.class.inc.php

@@ -64,83 +64,19 @@ class HTMLBulkExport extends TabularBulkExport
 
 	public function GetHeader()
 	{
-        $sData = '';
+		$sData = '';
 		
 		$oSet = new DBObjectSet($this->oSearch);
 		$this->aStatusInfo['status'] = 'running';
 		$this->aStatusInfo['position'] = 0;
 		$this->aStatusInfo['total'] = $oSet->Count();
 
-		$aSelectedClasses = $this->oSearch->GetSelectedClasses();
-		foreach($aSelectedClasses as $sAlias => $sClassName)
-		{
-			if (UserRights::IsActionAllowed($sClassName, UR_ACTION_BULK_READ, $oSet) && (UR_ALLOWED_YES || UR_ALLOWED_DEPENDS))
-			{
-				$aAuthorizedClasses[$sAlias] = $sClassName;
-			}
-		}
-		$aAliases = array_keys($aAuthorizedClasses);
-		$aData = array();
-		foreach($this->aStatusInfo['fields'] as $sExtendedAttCode)
-		{
-			if (preg_match('/^([^\.]+)\.(.+)$/', $sExtendedAttCode, $aMatches))
-			{
-				$sAlias = $aMatches[1];
-				$sAttCode = $aMatches[2];
-			}
-			else
-			{
-				
-				$sAlias = reset($aAliases);
-				$sAttCode = $sExtendedAttCode;
-			}
-			if (!in_array($sAlias, $aAliases))
-			{
-				throw new Exception("Invalid alias '$sAlias' for the column '$sExtendedAttCode'. Availables aliases: '".implode("', '", $aAliases)."'");
-			}
-			$sClass = $aSelectedClasses[$sAlias];
-				
-			switch($sAttCode)
-			{
-				case 'id':
-					if (count($aSelectedClasses) > 1)
-					{
-						$aData[] = $sAlias.'.id'; //@@@
-					}
-					else
-					{
-						$aData[] = 'id'; //@@@
-					}
-					break;
-
-				default:
-					$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
-					if (($oAttDef instanceof AttributeExternalField) || (($oAttDef instanceof AttributeFriendlyName) && ($oAttDef->GetKeyAttCode() != 'id')))
-					{
-						$oKeyAttDef = MetaModel::GetAttributeDef($sClass, $oAttDef->GetKeyAttCode());
-						$oExtAttDef = MetaModel::GetAttributeDef($oKeyAttDef->GetTargetClass(), $oAttDef->GetExtAttCode());
-						$sLabel =  $oKeyAttDef->GetLabel().'->'.$oExtAttDef->GetLabel();
-					}
-					else
-					{
-						$sLabel = $oAttDef->GetLabel();
-					}
-					if (count($aSelectedClasses) > 1)
-					{
-						$aData[] = $sAlias.'.'.$sLabel;
-					}
-					else
-					{
-						$aData[] = $sLabel;
-					}
-			}
-		}
 		$sData .= "<table class=\"listResults\">\n";
 		$sData .= "<thead>\n";
 		$sData .= "<tr>\n";
-		foreach($aData as $sLabel)
+		foreach($this->aStatusInfo['fields'] as $iCol => $aFieldSpec)
 		{
-			$sData .= "<th>".$sLabel."</th>\n";
+			$sData .= "<th>".$aFieldSpec['sColLabel']."</th>\n";
 		}
 		$sData .= "</tr>\n";
 		$sData .= "</thead>\n";
@@ -154,53 +90,18 @@ class HTMLBulkExport extends TabularBulkExport
 		$iPercentage = 0;
 
 		$oSet = new DBObjectSet($this->oSearch);
-		$aSelectedClasses = $this->oSearch->GetSelectedClasses();
-		$aAliases = array_keys($aSelectedClasses);
 		$oSet->SetLimit($this->iChunkSize, $this->aStatusInfo['position']);
+		$this->OptimizeColumnLoad($oSet);
 
-		$aAliasByField = array();
-		$aColumnsToLoad = array();
-
-		// Prepare the list of aliases / columns to load
-		foreach($this->aStatusInfo['fields'] as $sExtendedAttCode)
-		{
-			if (preg_match('/^([^\.]+)\.(.+)$/', $sExtendedAttCode, $aMatches))
-			{
-				$sAlias = $aMatches[1];
-				$sAttCode = $aMatches[2];
-			}
-			else
-			{
-				$sAlias = reset($aAliases);
-				$sAttCode = $sExtendedAttCode;
-			}
-				
-			if (!in_array($sAlias, $aAliases))
-			{
-				throw new Exception("Invalid alias '$sAlias' for the column '$sExtendedAttCode'. Availables aliases: '".implode("', '", $aAliases)."'");
-			}
-				
-			if (!array_key_exists($sAlias, $aColumnsToLoad))
-			{
-				$aColumnsToLoad[$sAlias] = array();
-			}
-			if ($sAttCode != 'id')
-			{
-				// id is not a real attribute code and, moreover, is always loaded
-				$aColumnsToLoad[$sAlias][] = $sAttCode;
-			}
-			$aAliasByField[$sExtendedAttCode] = array('alias' => $sAlias, 'attcode' => $sAttCode);
-		}
+		$sFirstAlias = $this->oSearch->GetClassAlias();
 
 		$iCount = 0;
 		$sData = '';
-		$oSet->OptimizeColumnLoad($aColumnsToLoad);
 		$iPreviousTimeLimit = ini_get('max_execution_time');
 		$iLoopTimeLimit = MetaModel::GetConfig()->Get('max_execution_time_per_loop');
 		while($aRow = $oSet->FetchAssoc())
 		{
 			set_time_limit($iLoopTimeLimit);
-			$sFirstAlias = reset($aAliases);
 			$oMainObj = $aRow[$sFirstAlias];
 			$sHilightClass = '';
 			if ($oMainObj)
@@ -215,20 +116,23 @@ class HTMLBulkExport extends TabularBulkExport
 			{
 				$sData .= "<tr>";
 			}
-			foreach($aAliasByField as $aAttCode)
+			foreach($this->aStatusInfo['fields'] as $iCol => $aFieldSpec)
 			{
-				$oObj = $aRow[$aAttCode['alias']];
+				$sAlias = $aFieldSpec['sAlias'];
+				$sAttCode = $aFieldSpec['sAttCode'];
+
+				$oObj = $aRow[$sAlias];
 				$sField = '';
 				if ($oObj)
 				{
-					switch($aAttCode['attcode'])
+					switch($sAttCode)
 					{
 						case 'id':
-							$sField = $aRow[$aAttCode['alias']]->GetHyperlink();
+							$sField = $aRow[$sAlias]->GetHyperlink();
 							break;
 								
 						default:
-							$sField = $aRow[$aAttCode['alias']]->GetAsHtml($aAttCode['attcode']);
+							$sField = $aRow[$sAlias]->GetAsHtml($sAttCode);
 					}
 				}
 				$sValue = ($sField === '') ? '&nbsp;' : $sField;

+ 26 - 125
core/spreadsheetbulkexport.class.inc.php

@@ -61,13 +61,6 @@ class SpreadsheetBulkExport extends TabularBulkExport
 		}
 	}
 	
-	public function ReadParameters()
-	{
-		parent::ReadParameters();
-	
-		$this->aStatusInfo['localize'] = (utils::ReadParam('no_localize', 0) != 1);
-	}	
-	
 	protected function GetSampleData($oObj, $sAttCode)
 	{
 		if ($oObj)
@@ -88,83 +81,23 @@ class SpreadsheetBulkExport extends TabularBulkExport
 		$this->aStatusInfo['position'] = 0;
 		$this->aStatusInfo['total'] = $oSet->Count();
 
-		$aSelectedClasses = $this->oSearch->GetSelectedClasses();
 		$aData = array();
-		foreach($this->aStatusInfo['fields'] as $sExtendedAttCode)
+		foreach($this->aStatusInfo['fields'] as $iCol => $aFieldSpec)
 		{
-			if (preg_match('/^([^\.]+)\.(.+)$/', $sExtendedAttCode, $aMatches))
-			{
-				$sAlias = $aMatches[1];
-				$sAttCode = $aMatches[2];
-			}
-			else
+			$sColLabel = $aFieldSpec['sColLabel'];
+			if ($aFieldSpec['sAttCode'] != 'id')
 			{
-				$sAlias = $this->oSearch->GetClassAlias();
-				$sAttCode = $sExtendedAttCode;
-			}
-			if (!array_key_exists($sAlias, $aSelectedClasses))
-			{
-				throw new Exception("Invalid alias '$sAlias' for the column '$sExtendedAttCode'. Availables aliases: '".implode("', '", array_keys($aSelectedClasses))."'");
-			}
-			$sClass = $aSelectedClasses[$sAlias];
-
-			switch($sAttCode)
-			{
-				case 'id':
-					if (count($aSelectedClasses) > 1)
-					{
-						$aData[] = $sAlias.'.id'; //@@@
-					}
-					else
-					{
-						$aData[] = 'id'; //@@@
-					}
-					break;
-
-				default:
-					$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
-					if (($oAttDef instanceof AttributeExternalField) || (($oAttDef instanceof AttributeFriendlyName) && ($oAttDef->GetKeyAttCode() != 'id')))
-					{
-						$oKeyAttDef = MetaModel::GetAttributeDef($sClass, $oAttDef->GetKeyAttCode());
-						$oExtAttDef = MetaModel::GetAttributeDef($oKeyAttDef->GetTargetClass(), $oAttDef->GetExtAttCode());
-						if ($this->aStatusInfo['localize'])
-						{
-							$sColLabel = $oKeyAttDef->GetLabel().'->'.$oExtAttDef->GetLabel();
-						}
-						else
-						{
-							$sColLabel = $oKeyAttDef->GetCode().'->'.$oExtAttDef->GetCode();
-						}
-					}
-					else
-					{
-						$sColLabel = $this->aStatusInfo['localize'] ? $oAttDef->GetLabel() : $sAttCode;
-					}
-					$oFinalAttDef = $oAttDef->GetFinalAttDef();
-					if (get_class($oFinalAttDef) == 'AttributeDateTime')
-					{
-						if (count($aSelectedClasses) > 1)
-						{
-							$aData[] = $sAlias.'.'.$sColLabel.' ('.Dict::S('UI:SplitDateTime-Date').')';
-							$aData[] = $sAlias.'.'.$sColLabel.' ('.Dict::S('UI:SplitDateTime-Time').')';
-						}
-						else
-						{
-							$aData[] = $sColLabel.' ('.Dict::S('UI:SplitDateTime-Date').')';
-							$aData[] = $sColLabel.' ('.Dict::S('UI:SplitDateTime-Time').')';
-						}
-					}
-					else
-					{
-						if (count($aSelectedClasses) > 1)
-						{
-							$aData[] = $sAlias.'.'.$sColLabel;
-						}
-						else
-						{
-							$aData[] = $sColLabel;
-						}
-					}
+				$oAttDef = MetaModel::GetAttributeDef($aFieldSpec['sClass'], $aFieldSpec['sAttCode']);
+				$oFinalAttDef = $oAttDef->GetFinalAttDef();
+				if (get_class($oFinalAttDef) == 'AttributeDateTime')
+				{
+					$aData[] = $sColLabel.' ('.Dict::S('UI:SplitDateTime-Date').')';
+					$aData[] = $sColLabel.' ('.Dict::S('UI:SplitDateTime-Time').')';
+				}
+				else
+				{
+					$aData[] = $sColLabel;
+				}
 			}
 		}
 		$sData = "<table border=\"1\">\n";
@@ -183,46 +116,11 @@ class SpreadsheetBulkExport extends TabularBulkExport
 		$iPercentage = 0;
 
 		$oSet = new DBObjectSet($this->oSearch);
-		$aSelectedClasses = $this->oSearch->GetSelectedClasses();
 		$oSet->SetLimit($this->iChunkSize, $this->aStatusInfo['position']);
-
-		$aAliasByField = array();
-		$aColumnsToLoad = array();
-
-		// Prepare the list of aliases / columns to load
-		foreach($this->aStatusInfo['fields'] as $sExtendedAttCode)
-		{
-			if (preg_match('/^([^\.]+)\.(.+)$/', $sExtendedAttCode, $aMatches))
-			{
-				$sAlias = $aMatches[1];
-				$sAttCode = $aMatches[2];
-			}
-			else
-			{
-				$sAlias = $this->oSearch->GetClassAlias();
-				$sAttCode = $sExtendedAttCode;
-			}
-
-			if (!array_key_exists($sAlias, $aSelectedClasses))
-			{
-				throw new Exception("Invalid alias '$sAlias' for the column '$sExtendedAttCode'. Availables aliases: '".implode("', '", array_keys($aSelectedClasses))."'");
-			}
-
-			if (!array_key_exists($sAlias, $aColumnsToLoad))
-			{
-				$aColumnsToLoad[$sAlias] = array();
-			}
-			if ($sAttCode != 'id')
-			{
-				// id is not a real attribute code and, moreover, is always loaded
-				$aColumnsToLoad[$sAlias][] = $sAttCode;
-			}
-			$aAliasByField[$sExtendedAttCode] = array('alias' => $sAlias, 'attcode' => $sAttCode);
-		}
+		$this->OptimizeColumnLoad($oSet);
 
 		$iCount = 0;
 		$sData = '';
-		$oSet->OptimizeColumnLoad($aColumnsToLoad);
 		$iPreviousTimeLimit = ini_get('max_execution_time');
 		$iLoopTimeLimit = MetaModel::GetConfig()->Get('max_execution_time_per_loop');
 		while($aRow = $oSet->FetchAssoc())
@@ -230,17 +128,20 @@ class SpreadsheetBulkExport extends TabularBulkExport
 			set_time_limit($iLoopTimeLimit);
 
 			$sData .= "<tr>";
-			foreach($aAliasByField as $aAttCode)
+			foreach($this->aStatusInfo['fields'] as $iCol => $aFieldSpec)
 			{
+				$sAlias = $aFieldSpec['sAlias'];
+				$sAttCode = $aFieldSpec['sAttCode'];
+
 				$sField = '';
-				$oObj = $aRow[$aAttCode['alias']];
+				$oObj = $aRow[$sAlias];
 				if ($oObj == null)
 				{
 					$sData .= "<td x:str>$sField</td>";
 					continue;
 				}
 				
-				switch($aAttCode['attcode'])
+				switch($sAttCode)
 				{
 					case 'id':
 					$sField = $oObj->GetName();
@@ -248,25 +149,25 @@ class SpreadsheetBulkExport extends TabularBulkExport
 					break;
 							
 					default:
-					$oAttDef = MetaModel::GetAttributeDef(get_class($oObj), $aAttCode['attcode']);
+					$oAttDef = MetaModel::GetAttributeDef(get_class($oObj), $sAttCode);
 					$oFinalAttDef = $oAttDef->GetFinalAttDef();
 					if (get_class($oFinalAttDef) == 'AttributeDateTime')
 					{
-						$iDate = AttributeDateTime::GetAsUnixSeconds($oObj->Get($aAttCode['attcode']));
+						$iDate = AttributeDateTime::GetAsUnixSeconds($oObj->Get($sAttCode));
 						$sData .= '<td>'.date('Y-m-d', $iDate).'</td>'; // Add the first column directly
 						$sField = date('H:i:s', $iDate); // Will add the second column below
 						$sData .= "<td>$sField</td>";
 					}
 					else if($oAttDef instanceof AttributeCaseLog)
 					{
-						$rawValue = $oObj->Get($aAttCode['attcode']);
+						$rawValue = $oObj->Get($sAttCode);
 						$sField = str_replace("\n", "<br/>", htmlentities($rawValue->__toString(), ENT_QUOTES, 'UTF-8'));
 						// Trick for Excel: treat the content as text even if it begins with an equal sign
 						$sData .= "<td x:str>$sField</td>";
 					}
 					else
 					{
-						$rawValue = $oObj->Get($aAttCode['attcode']);
+						$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)
@@ -280,7 +181,7 @@ class SpreadsheetBulkExport extends TabularBulkExport
 								}
 							}
 						}
-						if ($this->aStatusInfo['localize'])
+						if ($this->bLocalizeOutput)
 						{
 							$sField = htmlentities($oFinalAttDef->GetEditValue($rawValue), ENT_QUOTES, 'UTF-8');
 						}

+ 104 - 3
core/tabularbulkexport.class.inc.php

@@ -310,12 +310,113 @@ EOF
 			}
 		}
 
+		// Interpret (and check) the list of fields
+		//
+		$aSelectedClasses = $this->oSearch->GetSelectedClasses();
+		$aAliases = array_keys($aSelectedClasses);
+		$aAuthorizedClasses = array();
+		foreach($aSelectedClasses as $sAlias => $sClassName)
+		{
+			if (UserRights::IsActionAllowed($sClassName, UR_ACTION_BULK_READ) == UR_ALLOWED_YES)
+			{
+				$aAuthorizedClasses[$sAlias] = $sClassName;
+			}
+		}
 		$aFields = explode(',', $sFields);
 		$this->aStatusInfo['fields'] = array();
-		foreach($aFields as $sField)
+		foreach($aFields as $sFieldSpec)
 		{
-			// Trim the values since it's too temping to write: fields=name, first_name, org_name instead of fields=name,first_name,org_name
-			$this->aStatusInfo['fields'][] = trim($sField);
+			// Trim the values since it's natural to write: fields=name, first_name, org_name instead of fields=name,first_name,org_name
+			$sExtendedAttCode = trim($sFieldSpec);
+
+			if (preg_match('/^([^\.]+)\.(.+)$/', $sExtendedAttCode, $aMatches))
+			{
+				$sAlias = $aMatches[1];
+				$sAttCode = $aMatches[2];
+			}
+			else
+			{
+				$sAlias = reset($aAliases);
+				$sAttCode = $sExtendedAttCode;
+			}
+			if (!array_key_exists($sAlias, $aSelectedClasses))
+			{
+				throw new Exception("Invalid alias '$sAlias' for the column '$sExtendedAttCode'. Availables aliases: '".implode("', '", $aAliases)."'");
+			}
+			$sClass = $aSelectedClasses[$sAlias];
+			if (!array_key_exists($sAlias, $aAuthorizedClasses))
+			{
+				throw new Exception("You do not have enough permissions to bulk read data of class '$sClass' (alias: $sAlias)");
+			}
+				
+			switch($sAttCode)
+			{
+				case 'id':
+				$sLabel = 'id';
+				$oAttDef = null;
+				break;
+						
+				default:
+				$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
+				if (($oAttDef instanceof AttributeExternalField) || (($oAttDef instanceof AttributeFriendlyName) && ($oAttDef->GetKeyAttCode() != 'id')))
+				{
+					$oKeyAttDef = MetaModel::GetAttributeDef($sClass, $oAttDef->GetKeyAttCode());
+					$oExtAttDef = MetaModel::GetAttributeDef($oKeyAttDef->GetTargetClass(), $oAttDef->GetExtAttCode());
+					if ($this->bLocalizeOutput)
+					{
+						$sLabel = $oKeyAttDef->GetLabel().'->'.$oExtAttDef->GetLabel();
+					}
+					else
+					{
+						$sLabel =  $oKeyAttDef->GetCode().'->'.$oExtAttDef->GetCode();
+					}						
+				}
+				else
+				{
+					$sLabel = $this->bLocalizeOutput ? $oAttDef->GetLabel() : $sAttCode;
+				}
+			}
+			if (count($aAuthorizedClasses) > 1)
+			{
+				$sColLabel = $sAlias.'.'.$sLabel;
+			}
+			else
+			{
+				$sColLabel = $sLabel;
+			}
+			$this->aStatusInfo['fields'][] = array(
+				'sFieldSpec' => $sExtendedAttCode,
+				'sAlias' => $sAlias,
+				'sClass' => $sClass,
+				'sAttCode' => $sAttCode,
+				'sLabel' => $sLabel,
+				'sColLabel' => $sColLabel
+			);
 		}
 	}
+
+	/**
+	 * Prepare the given object set with the list of fields as read into $this->aStatusInfo['fields']
+	 */
+	protected function OptimizeColumnLoad(DBObjectSet $oSet)
+	{
+		$aColumnsToLoad = array();
+
+		foreach($this->aStatusInfo['fields'] as $iCol => $aFieldSpec)
+		{
+			$sAlias = $aFieldSpec['sAlias'];
+			$sAttCode = $aFieldSpec['sAttCode'];
+				
+			if (!array_key_exists($sAlias, $aColumnsToLoad))
+			{
+				$aColumnsToLoad[$sAlias] = array();
+			}
+			if ($sAttCode != 'id')
+			{
+				// id is not a real attribute code and, moreover, is always loaded
+				$aColumnsToLoad[$sAlias][] = $sAttCode;
+			}
+		}
+		$oSet->OptimizeColumnLoad($aColumnsToLoad);
+	}	 	
 }

+ 10 - 4
core/xmlbulkexport.class.inc.php

@@ -65,7 +65,6 @@ class XMLBulkExport extends BulkExport
 	{
 		parent::ReadParameters();
 	
-		$this->aStatusInfo['localize'] = (utils::ReadParam('no_localize', 0) != 1);
 		$this->aStatusInfo['linksets'] = (utils::ReadParam('linksets', 0) == 1);
 	}
 	
@@ -84,6 +83,15 @@ class XMLBulkExport extends BulkExport
 
 	public function GetHeader()
 	{
+		// Check permissions
+		foreach($this->oSearch->GetSelectedClasses() as $sAlias => $sClass)
+		{
+			if (UserRights::IsActionAllowed($sClass, UR_ACTION_BULK_READ) != UR_ALLOWED_YES)
+			{
+				throw new Exception("You do not have enough permissions to bulk read data of class '$sClass' (alias: $sAlias)");
+			}
+		}
+
 		$oSet = new DBObjectSet($this->oSearch);
 		$this->aStatusInfo['position'] = 0;
 		$this->aStatusInfo['total'] = $oSet->Count();
@@ -102,8 +110,6 @@ class XMLBulkExport extends BulkExport
 		$oSet = new DBObjectSet($this->oSearch);
 		$oSet->SetLimit($this->iChunkSize, $this->aStatusInfo['position']);
 		
-		$bLocalize = $this->aStatusInfo['localize'];
-		
 		$aClasses = $this->oSearch->GetSelectedClasses();
 		$aAuthorizedClasses = array();
 		$aClass2Attributes = array();
@@ -170,7 +176,7 @@ class XMLBulkExport extends BulkExport
 					}
 					else
 					{
-						$sValue = $oObj->GetAsXML($sAttCode, $bLocalize);
+						$sValue = $oObj->GetAsXML($sAttCode, $this->bLocalizeOutput);
 						$sData .= "<$sAttCode>$sValue</$sAttCode>\n";
 					}
 				}